package org.edx.mobile.test.http; import com.google.gson.Gson; import org.edx.mobile.http.OkHttpUtil; import org.edx.mobile.model.Filter; import org.edx.mobile.model.api.AnnouncementsModel; import org.edx.mobile.model.api.EnrolledCoursesResponse; import org.edx.mobile.model.api.HandoutModel; import org.edx.mobile.model.api.ResetPasswordResponse; import org.edx.mobile.model.api.SectionEntry; import org.edx.mobile.model.api.SyncLastAccessedSubsectionResponse; import org.edx.mobile.model.api.VideoResponseModel; import org.edx.mobile.model.course.BlockPath; import org.edx.mobile.model.course.BlockType; import org.edx.mobile.model.course.CourseComponent; import org.edx.mobile.model.course.DiscussionBlockModel; import org.edx.mobile.model.course.DiscussionData; import org.edx.mobile.model.course.HasDownloadEntry; import org.edx.mobile.model.course.IBlock; import org.edx.mobile.model.course.VideoBlockModel; import org.edx.mobile.model.course.VideoData; import org.edx.mobile.module.registration.model.RegistrationDescription; import org.edx.mobile.test.util.MockDataUtil; import org.json.JSONException; import org.json.JSONObject; import org.junit.Ignore; import org.junit.Test; import java.io.IOException; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import retrofit2.Response; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; /** * This class contains unit tests for API calls to server. * <p/> * Note: We aren't actually calling a live server for responses * as we already have a mock server implemented. */ public class ApiTests extends HttpBaseTestCase { @Override public void setUp() throws Exception { super.setUp(); } // TODO: Debug and fix test failure @Ignore @Test public void testSyncLastSubsection() throws Exception { login(); EnrolledCoursesResponse e = api.getEnrolledCourses().get(0); Map<String, SectionEntry> map = api.getCourseHierarchy(e.getCourse().getId(), false); Entry<String, SectionEntry> entry = map.entrySet().iterator().next(); Entry<String, ArrayList<VideoResponseModel>> subsection = entry.getValue().sections.entrySet().iterator().next(); String courseId = e.getCourse().getId(); String lastVisitedModuleId = subsection.getValue().get(0).getSection().getId(); assertNotNull(courseId); assertNotNull(lastVisitedModuleId); print(String.format("course= %s ; sub-section= %s", courseId, lastVisitedModuleId)); // TODO: lastVisitedModuleId must be section.id (id is now available) SyncLastAccessedSubsectionResponse model = api.syncLastAccessedSubsection(courseId, lastVisitedModuleId); assertNotNull(model); print("sync returned: " + model.last_visited_module_id); } // TODO: Debug and fix test failure @Ignore @Test public void testGetLastAccessedModule() throws Exception { login(); EnrolledCoursesResponse e = api.getEnrolledCourses().get(0); String courseId = e.getCourse().getId(); assertNotNull(courseId); print(String.format("course= %s", courseId)); SyncLastAccessedSubsectionResponse model = api.getLastAccessedSubsection(courseId); assertNotNull(model); // print(model.json); } @Test public void testResetPassword() throws Exception { print("test: reset password"); Response<ResetPasswordResponse> response = loginService.resetPassword("user@edx.org").execute(); assertTrue(response.isSuccessful()); ResetPasswordResponse model = response.body(); assertTrue(model != null); print(model.value); print("test: finished: reset password"); } // TODO: Debug and fix test failure @Ignore @Test public void testHandouts() throws Exception { login(); // get a course id for this test List<EnrolledCoursesResponse> courses = api.getEnrolledCourses(); assertTrue("Must have enrolled to at least one course", courses != null && courses.size() > 0); String handoutURL = courses.get(0).getCourse().getCourse_handouts(); HandoutModel model = api.getHandout(handoutURL, false); assertTrue(model != null); print(model.handouts_html); } @Test public void testChannelId() throws Exception { login(); // get a course id for this test List<EnrolledCoursesResponse> courses = api.getEnrolledCourses(); assertTrue("Must have enrolled to at least one course", courses != null && courses.size() > 0); String subscription_id = courses.get(0).getCourse().getSubscription_id(); //should the channelId be mandatory? assertTrue(subscription_id != null); } // TODO: Debug and fix test failure @Ignore @Test public void testCourseStructure() throws Exception { login(); // get a course id for this test List<EnrolledCoursesResponse> courses = api.getEnrolledCourses(); assertTrue("Must have enrolled to at least one course", courses != null && courses.size() > 0); String courseId = courses.get(0).getCourse().getId(); Map<String, SectionEntry> chapters = api.getCourseHierarchy(courseId, false); for (Entry<String, SectionEntry> entry : chapters.entrySet()) { print("---------------" + entry.getKey() + "---------------"); for (Entry<String, ArrayList<VideoResponseModel>> se : entry.getValue().sections.entrySet()) { print("------------" + se.getKey() + "------------"); for (VideoResponseModel v : se.getValue()) { print(v.getSummary().getDisplayName()); } } } } @Test @Override public void login() throws Exception { super.login(); } // TODO: Debug and fix test failure @Ignore @Test public void testGetAnnouncement() throws Exception { login(); // get a course id for this test List<EnrolledCoursesResponse> courses = api.getEnrolledCourses(); assertTrue("Must have enrolled to at least one course", courses != null && courses.size() > 0); String updatesUrl = courses.get(0).getCourse().getCourse_updates(); List<AnnouncementsModel> res = api.getAnnouncement(updatesUrl, false); assertTrue(res != null); for (AnnouncementsModel r : res) { print(r.getDate()); } } @Test public void testReadRegistrationDescription() throws Exception { RegistrationDescription form = api.getRegistrationDescription(); assertNotNull(form); assertNotNull(form.getEndpoint()); assertNotNull(form.getMethod()); assertNotNull(form.getFields()); assertTrue(form.getFields().size() > 0); // verify if enum type is parsed assertNotNull(form.getFields().get(0).getFieldType()); } // TODO: Debug and fix test failure @Ignore @Test public void testEnrollInACourse() throws Exception { login(); print("test: Enroll in a course"); EnrolledCoursesResponse e = api.getEnrolledCourses().get(0); String courseId = e.getCourse().getId(); boolean success = api.enrollInACourse(courseId, true); assertTrue(success); print("success"); print("test: finished: reset password"); } @Test public void testGetCourseStructure() throws Exception { login(); // General overall testing of CourseComponent API without recursion EnrolledCoursesResponse e = api.getEnrolledCourses().get(0); final String courseId = e.getCourse().getId(); final CourseComponent courseComponent = serviceManager.getCourseStructure(courseId, OkHttpUtil.REQUEST_CACHE_TYPE.IGNORE_CACHE); assertNotNull(courseComponent); assertNotNull(courseComponent.getRoot()); assertEquals(courseId, courseComponent.getCourseId()); List<IBlock> children = courseComponent.getChildren(); assertNotNull(children); List<CourseComponent> childContainers = new ArrayList<>(); List<CourseComponent> childLeafs = new ArrayList<>(); for (IBlock c : children) { assertTrue(c instanceof CourseComponent); final CourseComponent child = (CourseComponent) c; assertEquals(child, courseComponent.find(new Filter<CourseComponent>() { @Override public boolean apply(CourseComponent component) { return child.getId().equals(component.getId()); } })); List<IBlock> grandchildren = child.getChildren(); for (IBlock gc : grandchildren) { assertTrue(gc instanceof CourseComponent); final CourseComponent grandchild = (CourseComponent) c; assertEquals(grandchild, courseComponent.find(new Filter<CourseComponent>() { @Override public boolean apply(CourseComponent component) { return grandchild.getId().equals(component.getId()); } })); } assertNull(child.find(new Filter<CourseComponent>() { @Override public boolean apply(CourseComponent component) { return courseComponent.getId().equals(component.getId()); } })); if (child.isContainer()) { childContainers.add(child); } else { childLeafs.add(child); } } assertEquals(childContainers, courseComponent.getChildContainers()); assertEquals(childLeafs, courseComponent.getChildLeafs()); assertTrue(courseComponent.isLastChild()); int childrenSize = children.size(); assertTrue(childrenSize > 0); assertTrue(((CourseComponent) children.get(childrenSize - 1)).isLastChild()); BlockType blockType = courseComponent.getType(); assertSame(courseComponent, courseComponent.getAncestor(Integer.MAX_VALUE)); assertSame(courseComponent, courseComponent.getAncestor(EnumSet.of(blockType))); List<VideoBlockModel> videos = courseComponent.getVideos(); assertNotNull(videos); for (HasDownloadEntry video : videos) { assertNotNull(video); assertTrue(video instanceof CourseComponent); CourseComponent videoComponent = (CourseComponent) video; assertFalse(videoComponent.isContainer()); assertEquals(BlockType.VIDEO, videoComponent.getType()); } for (BlockType type : BlockType.values()) { EnumSet<BlockType> typeSet = EnumSet.of(type); List<CourseComponent> typeComponents = new ArrayList<>(); courseComponent.fetchAllLeafComponents(typeComponents, typeSet); for (CourseComponent typeComponent : typeComponents) { assertEquals(type, typeComponent.getType()); verifyModelParsing(typeComponent); } if (type != blockType) { assertNotSame(courseComponent, courseComponent.getAncestor(EnumSet.of(type))); } } BlockPath path = courseComponent.getPath(); assertNotNull(path); assertEquals(1, path.getPath().size()); assertSame(courseComponent, path.get(0)); List<CourseComponent> leafComponents = new ArrayList<>(); courseComponent.fetchAllLeafComponents(leafComponents, EnumSet.allOf(BlockType.class)); for (CourseComponent leafComponent : leafComponents) { BlockPath leafPath = leafComponent.getPath(); assertNotNull(leafPath); int pathSize = leafPath.getPath().size(); assertTrue(pathSize > 1); CourseComponent component = leafComponent; for (int i = pathSize - 1; i >= 0; i--) { assertSame(component, leafPath.get(i)); component = component.getParent(); } } } /** * Verifies the parsing of course structure json to {@link CourseComponent} model class. * * @param component Parsed {@link CourseComponent} model. */ private void verifyModelParsing(CourseComponent component) throws IOException, JSONException { JSONObject jsonObj; jsonObj = new JSONObject(MockDataUtil.getMockResponse("get_course_structure")); jsonObj = jsonObj.getJSONObject("blocks"); jsonObj = jsonObj.getJSONObject(component.getId()); assertNotNull(jsonObj); // Not using the getDisplayName function below, because it returns a placeholder text // when the display_name field's value is empty. assertEquals(jsonObj.getString("display_name"), component.getInternalName()); assertEquals(jsonObj.getBoolean("graded"), component.isGraded()); assertEquals(jsonObj.getString("student_view_url"), component.getBlockUrl()); assertEquals(jsonObj.getBoolean("student_view_multi_device"), component.isMultiDevice()); assertEquals(jsonObj.getString("lms_web_url"), component.getWebUrl()); // Type specific validations Gson gson = new Gson(); switch (component.getType()) { case VIDEO: { JSONObject dataObj = jsonObj.getJSONObject("student_view_data"); // Our current parser checks the existence of these fields to determine the type to convert into assertTrue(dataObj.has("encoded_videos") || dataObj.has("transcripts")); String dataRawJson = dataObj.toString(); assertTrue(component instanceof VideoBlockModel); VideoBlockModel model = (VideoBlockModel) component; VideoData expected = gson.fromJson(dataRawJson, VideoData.class); assertEquals(expected, model.getData()); break; } case DISCUSSION: { JSONObject dataObj = jsonObj.getJSONObject("student_view_data"); // Our current parser checks the existence of these fields to determine the type to convert into assertTrue(dataObj.has("topic_id")); String dataRawJson = dataObj.toString(); assertTrue(component instanceof DiscussionBlockModel); DiscussionBlockModel model = (DiscussionBlockModel) component; DiscussionData expected = gson.fromJson(dataRawJson, DiscussionData.class); assertEquals(expected, model.getData()); break; } } } }