1#![allow(dead_code)]
2
3use crate::{TestMyCodeClient, TestMyCodeClientError, request::*, response::*};
6use http::Method;
7use oauth2::TokenResponse;
8use reqwest::blocking::{
9 RequestBuilder, Response,
10 multipart::{Form, Part},
11};
12use serde::de::DeserializeOwned;
13use std::{
14 collections::HashMap,
15 io::{Read, Write},
16};
17use tmc_langs_plugins::Language;
18use tmc_langs_util::deserialize;
19use url::Url;
20
21pub enum PasteData {
22 WithoutMessage,
23 WithMessage(String),
24}
25
26pub enum ReviewData {
27 WithoutMessage,
28 WithMessage(String),
29}
30
31fn make_url(
33 client: &TestMyCodeClient,
34 tail: impl AsRef<str>,
35) -> Result<Url, TestMyCodeClientError> {
36 client
37 .0
38 .root_url
39 .join(tail.as_ref())
40 .map_err(|e| TestMyCodeClientError::UrlParse(tail.as_ref().to_string(), e))
41}
42
43fn percent_encode(target: &str) -> String {
45 percent_encoding::utf8_percent_encode(target, percent_encoding::NON_ALPHANUMERIC).to_string()
46}
47
48fn prepare_tmc_request(client: &TestMyCodeClient, method: Method, url: Url) -> RequestBuilder {
50 log::info!("{method} {url}");
51 let req = client.0.client.request(method, url).query(&[
52 ("client", &client.0.client_name),
53 ("client_version", &client.0.client_version),
54 ]);
55 if let Some(token) = &client.0.token {
56 req.bearer_auth(token.access_token().secret())
57 } else {
58 req
59 }
60}
61
62fn assert_success(response: Response, url: &Url) -> Result<Response, TestMyCodeClientError> {
64 let status = response.status();
65 if status.is_success() {
66 Ok(response)
67 } else if let Ok(err) = response.json::<ErrorResponse>() {
68 let error = match (err.error, err.errors) {
70 (Some(err), Some(errs)) => format!("{}, {}", err, errs.join(",")),
71 (Some(err), None) => err,
72 (None, Some(errs)) => errs.join(","),
73 (None, None) => "".to_string(),
74 };
75 Err(TestMyCodeClientError::HttpError {
76 url: url.clone(),
77 status,
78 error,
79 obsolete_client: err.obsolete_client,
80 })
81 } else {
82 Err(TestMyCodeClientError::HttpError {
84 url: url.clone(),
85 status,
86 error: status.to_string(),
87 obsolete_client: false,
88 })
89 }
90}
91
92fn assert_success_json<T: DeserializeOwned>(
93 res: Response,
94 url: &Url,
95) -> Result<T, TestMyCodeClientError> {
96 let res = assert_success(res, url)?
97 .bytes()
98 .map_err(TestMyCodeClientError::HttpReadResponse)?;
99 let json = deserialize::json_from_slice(&res)
100 .map_err(|e| TestMyCodeClientError::HttpJsonResponse(url.clone(), e))?;
101 Ok(json)
102}
103
104pub fn prepare_feedback_form(feedback: Vec<FeedbackAnswer>) -> HashMap<String, String> {
106 let mut form = HashMap::new();
107 for (i, answer) in feedback.into_iter().enumerate() {
108 form.insert(
109 format!("answers[{i}][question_id]"),
110 answer.question_id.to_string(),
111 );
112 form.insert(format!("answers[{i}][answer]"), answer.answer);
113 }
114 form
115}
116
117pub fn download(
119 client: &TestMyCodeClient,
120 url: Url,
121 target: &mut dyn Write,
122) -> Result<(), TestMyCodeClientError> {
123 let res = prepare_tmc_request(client, Method::GET, url.clone())
124 .send()
125 .map_err(|e| TestMyCodeClientError::ConnectionError(Method::GET, url.clone(), e))?;
126
127 let mut res = assert_success(res, &url)?;
128 let _bytes = res
129 .copy_to(target)
130 .map_err(TestMyCodeClientError::HttpWriteResponse)?;
131 Ok(())
132}
133
134pub fn get_json<T: DeserializeOwned>(
136 client: &TestMyCodeClient,
137 url: Url,
138 params: &[(&str, String)],
139) -> Result<T, TestMyCodeClientError> {
140 let res = prepare_tmc_request(client, Method::GET, url.clone())
141 .query(params)
142 .send()
143 .map_err(|e| TestMyCodeClientError::ConnectionError(Method::GET, url.clone(), e))?;
144
145 let json = assert_success_json(res, &url)?;
146 Ok(json)
147}
148
149pub fn post_form<T: DeserializeOwned>(
151 client: &TestMyCodeClient,
152 url: Url,
153 form: &HashMap<String, String>,
154) -> Result<T, TestMyCodeClientError> {
155 let res = prepare_tmc_request(client, Method::POST, url.clone())
156 .form(form)
157 .send()
158 .map_err(|e| TestMyCodeClientError::ConnectionError(Method::GET, url.clone(), e))?;
159
160 let json = assert_success_json(res, &url)?;
161 Ok(json)
162}
163
164pub fn get_credentials(client: &TestMyCodeClient) -> Result<Credentials, TestMyCodeClientError> {
167 let client_name = &client.0.client_name;
168 let url = make_url(
169 client,
170 format!("/api/v8/application/{client_name}/credentials"),
171 )?;
172 get_json(client, url, &[])
173}
174
175pub fn get_submission(
178 client: &TestMyCodeClient,
179 submission_id: u32,
180) -> Result<SubmissionProcessingStatus, TestMyCodeClientError> {
181 let url = make_url(client, format!("/api/v8/core/submissions/{submission_id}"))?;
182 get_json(client, url, &[])
183}
184
185pub mod user {
186 use super::*;
187
188 pub fn get(client: &TestMyCodeClient, user_id: u32) -> Result<User, TestMyCodeClientError> {
191 let url = make_url(client, format!("/api/v8/users/{user_id}"))?;
192 get_json(client, url, &[])
193 }
194
195 pub fn get_current(client: &TestMyCodeClient) -> Result<User, TestMyCodeClientError> {
198 let url = make_url(client, "/api/v8/users/current")?;
199 get_json(client, url, &[])
200 }
201
202 pub fn get_basic_info_by_usernames(
206 client: &TestMyCodeClient,
207 usernames: &[String],
208 ) -> Result<Vec<User>, TestMyCodeClientError> {
209 let url = make_url(client, "/api/v8/users/basic_info_by_usernames")?;
210 let mut username_map = HashMap::new();
211 username_map.insert("usernames", usernames);
212 let res = prepare_tmc_request(client, Method::POST, url.clone())
213 .json(&username_map)
214 .send()
215 .map_err(|e| TestMyCodeClientError::ConnectionError(Method::POST, url.clone(), e))?;
216
217 let json = assert_success_json(res, &url)?;
218 Ok(json)
219 }
220
221 pub fn get_basic_info_by_emails(
225 client: &TestMyCodeClient,
226 emails: &[String],
227 ) -> Result<Vec<User>, TestMyCodeClientError> {
228 let url = make_url(client, "/api/v8/users/basic_info_by_emails")?;
229 let mut email_map = HashMap::new();
230 email_map.insert("emails", emails);
231 let res = prepare_tmc_request(client, Method::POST, url.clone())
232 .json(&email_map)
233 .send()
234 .map_err(|e| TestMyCodeClientError::ConnectionError(Method::POST, url.clone(), e))?;
235
236 let json = assert_success_json(res, &url)?;
237 Ok(json)
238 }
239}
240
241pub mod course {
242 use super::*;
243
244 pub fn get_by_id(
247 client: &TestMyCodeClient,
248 course_id: u32,
249 ) -> Result<CourseData, TestMyCodeClientError> {
250 let url = make_url(client, format!("/api/v8/courses/{course_id}"))?;
251 get_json(client, url, &[])
252 }
253
254 pub fn get(
257 client: &TestMyCodeClient,
258 organization_slug: &str,
259 course_name: &str,
260 ) -> Result<CourseData, TestMyCodeClientError> {
261 let url = make_url(
262 client,
263 format!(
264 "/api/v8/org/{}/courses/{}",
265 percent_encode(organization_slug),
266 percent_encode(course_name)
267 ),
268 )?;
269 get_json(client, url, &[])
270 }
271}
272
273pub mod point {
274 use super::*;
275
276 pub fn get_course_points_by_id(
279 client: &TestMyCodeClient,
280 course_id: u32,
281 ) -> Result<Vec<CourseDataExercisePoint>, TestMyCodeClientError> {
282 let url = make_url(client, format!("/api/v8/courses/{course_id}/points"))?;
283 get_json(client, url, &[])
284 }
285
286 pub fn get_exercise_points_by_id(
289 client: &TestMyCodeClient,
290 course_id: u32,
291 exercise_name: &str,
292 ) -> Result<Vec<CourseDataExercisePoint>, TestMyCodeClientError> {
293 let url = make_url(
294 client,
295 format!(
296 "/api/v8/courses/{}/exercises/{}/points",
297 course_id,
298 percent_encode(exercise_name)
299 ),
300 )?;
301 get_json(client, url, &[])
302 }
303
304 pub fn get_exercise_points_for_user_by_id(
307 client: &TestMyCodeClient,
308 course_id: u32,
309 exercise_name: &str,
310 user_id: u32,
311 ) -> Result<Vec<CourseDataExercisePoint>, TestMyCodeClientError> {
312 let url = make_url(
313 client,
314 format!(
315 "/api/v8/courses/{}/exercises/{}/users/{}/points",
316 course_id,
317 percent_encode(exercise_name),
318 user_id
319 ),
320 )?;
321 get_json(client, url, &[])
322 }
323
324 pub fn get_exercise_points_for_current_user_by_id(
327 client: &TestMyCodeClient,
328 course_id: u32,
329 exercise_name: &str,
330 ) -> Result<Vec<CourseDataExercisePoint>, TestMyCodeClientError> {
331 let url = make_url(
332 client,
333 format!(
334 "/api/v8/courses/{}/exercises/{}/users/current/points",
335 course_id,
336 percent_encode(exercise_name)
337 ),
338 )?;
339 get_json(client, url, &[])
340 }
341
342 pub fn get_course_points_for_user_by_id(
345 client: &TestMyCodeClient,
346 course_id: u32,
347 user_id: u32,
348 ) -> Result<Vec<CourseDataExercisePoint>, TestMyCodeClientError> {
349 let url = make_url(
350 client,
351 format!("/api/v8/courses/{course_id}/users/{user_id}/points"),
352 )?;
353 get_json(client, url, &[])
354 }
355
356 pub fn get_course_points_for_current_user_by_id(
359 client: &TestMyCodeClient,
360 course_id: u32,
361 ) -> Result<Vec<CourseDataExercisePoint>, TestMyCodeClientError> {
362 let url = make_url(
363 client,
364 format!("/api/v8/courses/{course_id}/users/current/points"),
365 )?;
366 get_json(client, url, &[])
367 }
368
369 pub fn get_course_points(
372 client: &TestMyCodeClient,
373 organization_slug: &str,
374 course_name: &str,
375 ) -> Result<Vec<CourseDataExercisePoint>, TestMyCodeClientError> {
376 let url = make_url(
377 client,
378 format!(
379 "/api/v8/org/{}/courses/{}/points",
380 percent_encode(organization_slug),
381 percent_encode(course_name)
382 ),
383 )?;
384 get_json(client, url, &[])
385 }
386
387 pub fn get_exercise_points(
410 client: &TestMyCodeClient,
411 organization_slug: &str,
412 course_name: &str,
413 exercise_name: &str,
414 ) -> Result<Vec<CourseDataExercisePoint>, TestMyCodeClientError> {
415 let url = make_url(
416 client,
417 format!(
418 "/api/v8/org/{}/courses/{}/exercises/{}/points",
419 percent_encode(organization_slug),
420 percent_encode(course_name),
421 percent_encode(exercise_name)
422 ),
423 )?;
424 get_json(client, url, &[])
425 }
426
427 pub fn get_course_points_for_user(
476 client: &TestMyCodeClient,
477 organization_slug: &str,
478 course_name: &str,
479 user_id: u32,
480 ) -> Result<Vec<CourseDataExercisePoint>, TestMyCodeClientError> {
481 let url = make_url(
482 client,
483 format!(
484 "/api/v8/org/{}/courses/{}/users/{}/points",
485 percent_encode(organization_slug),
486 percent_encode(course_name),
487 user_id
488 ),
489 )?;
490 get_json(client, url, &[])
491 }
492
493 pub fn get_course_points_for_current_user(
496 client: &TestMyCodeClient,
497 organization_slug: &str,
498 course_name: &str,
499 ) -> Result<Vec<CourseDataExercisePoint>, TestMyCodeClientError> {
500 let url = make_url(
501 client,
502 format!(
503 "/api/v8/org/{}/courses/{}/users/current/points",
504 percent_encode(organization_slug),
505 percent_encode(course_name),
506 ),
507 )?;
508 get_json(client, url, &[])
509 }
510}
511
512pub mod submission {
513 use super::*;
514
515 pub fn get_course_submissions_by_id(
518 client: &TestMyCodeClient,
519 course_id: u32,
520 ) -> Result<Vec<Submission>, TestMyCodeClientError> {
521 let url = make_url(client, format!("/api/v8/courses/{course_id}/submissions"))?;
522 get_json(client, url, &[])
523 }
524
525 pub fn get_course_submissions_for_last_hour(
528 client: &TestMyCodeClient,
529 course_id: u32,
530 ) -> Result<Vec<u32>, TestMyCodeClientError> {
531 let url = make_url(
532 client,
533 format!("/api/v8/courses/{course_id}/submissions/last_hour"),
534 )?;
535 get_json(client, url, &[])
536 }
537
538 pub fn get_course_submissions_for_user_by_id(
541 client: &TestMyCodeClient,
542 course_id: u32,
543 user_id: u32,
544 ) -> Result<Vec<Submission>, TestMyCodeClientError> {
545 let url = make_url(
546 client,
547 format!("/api/v8/courses/{course_id}/users/{user_id}/submissions"),
548 )?;
549 get_json(client, url, &[])
550 }
551
552 pub fn get_course_submissions_for_current_user_by_id(
555 client: &TestMyCodeClient,
556 course_id: u32,
557 ) -> Result<Vec<Submission>, TestMyCodeClientError> {
558 let url = make_url(
559 client,
560 format!("/api/v8/courses/{course_id}/users/current/submissions",),
561 )?;
562 get_json(client, url, &[])
563 }
564
565 pub fn get_exercise_submissions_for_user(
568 client: &TestMyCodeClient,
569 exercise_id: u32,
570 user_id: u32,
571 ) -> Result<Vec<Submission>, TestMyCodeClientError> {
572 let url = make_url(
573 client,
574 format!("/api/v8/exercises/{exercise_id}/users/{user_id}/submissions"),
575 )?;
576 get_json(client, url, &[])
577 }
578
579 pub fn get_exercise_submissions_for_current_user(
582 client: &TestMyCodeClient,
583 exercise_id: u32,
584 ) -> Result<Vec<Submission>, TestMyCodeClientError> {
585 let url = make_url(
586 client,
587 format!("/api/v8/exercises/{exercise_id}/users/current/submissions"),
588 )?;
589 get_json(client, url, &[])
590 }
591
592 pub fn get_course_submissions(
595 client: &TestMyCodeClient,
596 organization_slug: &str,
597 course_name: &str,
598 ) -> Result<Vec<Submission>, TestMyCodeClientError> {
599 let url = make_url(
600 client,
601 format!(
602 "/api/v8/org/{}/courses/{}/submissions",
603 percent_encode(organization_slug),
604 percent_encode(course_name)
605 ),
606 )?;
607 get_json(client, url, &[])
608 }
609
610 pub fn get_course_submissions_for_user(
613 client: &TestMyCodeClient,
614 organization_slug: &str,
615 course_name: &str,
616 user_id: u32,
617 ) -> Result<Vec<Submission>, TestMyCodeClientError> {
618 let url = make_url(
619 client,
620 format!(
621 "/api/v8/org/{}/courses/{}/users/{}/submissions",
622 percent_encode(organization_slug),
623 percent_encode(course_name),
624 user_id
625 ),
626 )?;
627 get_json(client, url, &[])
628 }
629
630 pub fn get_course_submissions_for_current_user(
633 client: &TestMyCodeClient,
634 organization_slug: &str,
635 course_name: &str,
636 ) -> Result<Vec<Submission>, TestMyCodeClientError> {
637 let url = make_url(
638 client,
639 format!(
640 "/api/v8/org/{}/courses/{}/users/current/submissions",
641 percent_encode(organization_slug),
642 percent_encode(course_name),
643 ),
644 )?;
645 get_json(client, url, &[])
646 }
647}
648
649pub mod exercise {
650 use super::*;
651
652 pub fn get_course_exercises_by_id(
655 client: &TestMyCodeClient,
656 course_id: u32,
657 ) -> Result<Vec<CourseExercise>, TestMyCodeClientError> {
658 let url = make_url(client, format!("/api/v8/courses/{course_id}/exercises"))?;
659 get_json(client, url, &[])
660 }
661
662 pub fn get_exercise_submissions_for_user(
665 client: &TestMyCodeClient,
666 exercise_id: u32,
667 user_id: u32,
668 ) -> Result<Vec<Submission>, TestMyCodeClientError> {
669 let url = make_url(
670 client,
671 format!("/api/v8/exercises/{exercise_id}/users/{user_id}/submissions"),
672 )?;
673 get_json(client, url, &[])
674 }
675
676 pub fn get_exercise_submissions_for_current_user(
679 client: &TestMyCodeClient,
680 exercise_id: u32,
681 ) -> Result<Vec<Submission>, TestMyCodeClientError> {
682 let url = make_url(
683 client,
684 format!("/api/v8/exercises/{exercise_id}/users/current/submissions"),
685 )?;
686 get_json(client, url, &[])
687 }
688
689 pub fn get_course_exercises(
692 client: &TestMyCodeClient,
693 organization_slug: &str,
694 course_name: &str,
695 ) -> Result<Vec<CourseDataExercise>, TestMyCodeClientError> {
696 let url = make_url(
697 client,
698 format!(
699 "/api/v8/org/{}/courses/{}/exercises",
700 percent_encode(organization_slug),
701 percent_encode(course_name)
702 ),
703 )?;
704 get_json(client, url, &[])
705 }
706
707 pub fn download_course_exercise(
710 client: &TestMyCodeClient,
711 organization_slug: &str,
712 course_name: &str,
713 exercise_name: &str,
714 target: &mut dyn Write,
715 ) -> Result<(), TestMyCodeClientError> {
716 let url = make_url(
717 client,
718 format!(
719 "/api/v8/org/{}/courses/{}/exercises/{}/download",
720 percent_encode(organization_slug),
721 percent_encode(course_name),
722 percent_encode(exercise_name)
723 ),
724 )?;
725 download(client, url, target)
726 }
727}
728
729pub mod organization {
730 use super::*;
731
732 pub fn get_organizations(
735 client: &TestMyCodeClient,
736 ) -> Result<Vec<Organization>, TestMyCodeClientError> {
737 let url = make_url(client, "/api/v8/org.json")?;
738 get_json(client, url, &[])
739 }
740
741 pub fn get_organization(
744 client: &TestMyCodeClient,
745 organization_slug: &str,
746 ) -> Result<Organization, TestMyCodeClientError> {
747 let url = make_url(
748 client,
749 format!("/api/v8/org/{}.json", percent_encode(organization_slug)),
750 )?;
751 get_json(client, url, &[])
752 }
753}
754
755pub mod core {
756 use super::*;
757
758 pub fn get_course(
761 client: &TestMyCodeClient,
762 course_id: u32,
763 ) -> Result<CourseDetails, TestMyCodeClientError> {
764 let url = make_url(client, format!("/api/v8/core/courses/{course_id}"))?;
765 get_json(client, url, &[])
766 }
767
768 pub fn get_course_reviews(
771 client: &TestMyCodeClient,
772 course_id: u32,
773 ) -> Result<Vec<Review>, TestMyCodeClientError> {
774 let url = make_url(client, format!("/api/v8/core/courses/{course_id}/reviews"))?;
775 get_json(client, url, &[])
776 }
777
778 pub fn update_course_review(
783 client: &TestMyCodeClient,
784 course_id: u32,
785 review_id: u32,
786 review_body: Option<String>,
787 mark_as_read: Option<bool>,
788 ) -> Result<(), TestMyCodeClientError> {
789 let url = make_url(
790 client,
791 format!("/api/v8/core/courses/{course_id}/reviews/{review_id}"),
792 )?;
793
794 let mut form = HashMap::new();
795 if let Some(review_body) = review_body {
796 form.insert("review[review_body]", review_body);
797 }
798 if let Some(mark_as_read) = mark_as_read {
799 if mark_as_read {
800 form.insert("mark_as_read", "true".to_string());
801 } else {
802 form.insert("mark_as_unread", "true".to_string());
803 }
804 }
805 let res = prepare_tmc_request(client, Method::PUT, url.clone())
806 .form(&form)
807 .send()
808 .map_err(|e| TestMyCodeClientError::ConnectionError(Method::PUT, url.clone(), e))?;
809
810 assert_success(res, &url)?;
811 Ok(())
812 }
813
814 pub fn unlock_course(
818 client: &TestMyCodeClient,
819 course_id: u32,
820 ) -> Result<(), TestMyCodeClientError> {
821 let url = make_url(client, format!("/api/v8/core/courses/{course_id}/unlock"))?;
822 let res = prepare_tmc_request(client, Method::POST, url.clone())
823 .send()
824 .map_err(|e| TestMyCodeClientError::ConnectionError(Method::POST, url.clone(), e))?;
825
826 assert_success(res, &url)?;
827 Ok(())
828 }
829
830 pub fn download_exercise(
833 client: &TestMyCodeClient,
834 exercise_id: u32,
835 target: &mut dyn Write,
836 ) -> Result<(), TestMyCodeClientError> {
837 let url = make_url(
838 client,
839 format!("/api/v8/core/exercises/{exercise_id}/download"),
840 )?;
841 download(client, url, target)
842 }
843
844 pub fn get_exercise(
847 client: &TestMyCodeClient,
848 exercise_id: u32,
849 ) -> Result<ExerciseDetails, TestMyCodeClientError> {
850 let url = make_url(client, format!("/api/v8/core/exercises/{exercise_id}"))?;
851 get_json(client, url, &[])
852 }
853
854 pub fn get_exercise_details(
857 client: &TestMyCodeClient,
858 exercises: &[u32],
859 ) -> Result<Vec<ExercisesDetails>, TestMyCodeClientError> {
860 let url = make_url(client, "/api/v8/core/exercises/details")?;
861 let exercise_ids = (
862 "ids",
863 exercises
864 .iter()
865 .map(u32::to_string)
866 .collect::<Vec<_>>()
867 .join(","),
868 );
869
870 let res: ExercisesDetailsWrapper = get_json(client, url, &[exercise_ids])?;
871 Ok(res.exercises)
872 }
873
874 pub fn download_exercise_solution(
877 client: &TestMyCodeClient,
878 exercise_id: u32,
879 target: &mut dyn Write,
880 ) -> Result<(), TestMyCodeClientError> {
881 let url = make_url(
882 client,
883 format!("/api/v8/core/exercises/{exercise_id}/solution/download"),
884 )?;
885 download(client, url, target)
886 }
887
888 pub fn submit_exercise(
891 client: &TestMyCodeClient,
892 exercise_id: u32,
893 submission_zip: impl Read + Send + Sync + 'static,
894 submit_paste: Option<PasteData>,
895 submit_for_review: Option<ReviewData>,
896 locale: Option<Language>,
897 ) -> Result<NewSubmission, TestMyCodeClientError> {
898 let url = make_url(
899 client,
900 format!("/api/v8/core/exercises/{exercise_id}/submissions"),
901 )?;
902
903 let mut form = Form::new();
904 form = form.part(
905 "submission[file]",
906 Part::reader(submission_zip).file_name("submission.zip"),
907 );
908
909 if let Some(submit_paste) = submit_paste {
910 form = form.text("paste", "1");
911 if let PasteData::WithMessage(message) = submit_paste {
912 form = form.text("message_for_paste", message);
913 }
914 }
915 if let Some(submit_for_review) = submit_for_review {
916 form = form.text("request_review", "1");
917 if let ReviewData::WithMessage(message) = submit_for_review {
918 form = form.text("message_for_reviewer", message);
919 }
920 }
921
922 if let Some(locale) = locale {
923 form = form.text("error_msg_locale", locale.to_639_3());
924 }
925
926 let res = prepare_tmc_request(client, Method::POST, url.clone())
927 .multipart(form)
928 .send()
929 .map_err(|e| TestMyCodeClientError::ConnectionError(Method::POST, url.clone(), e))?;
930
931 let json = assert_success_json(res, &url)?;
932 Ok(json)
933 }
934
935 pub fn get_organization_courses(
938 client: &TestMyCodeClient,
939 organization_slug: &str,
940 ) -> Result<Vec<Course>, TestMyCodeClientError> {
941 let url = make_url(
942 client,
943 format!(
944 "/api/v8/core/org/{}/courses",
945 percent_encode(organization_slug)
946 ),
947 )?;
948 get_json(client, url, &[])
949 }
950
951 pub fn download_submission(
954 client: &TestMyCodeClient,
955 submission_id: u32,
956 target: &mut dyn Write,
957 ) -> Result<(), TestMyCodeClientError> {
958 let url = make_url(
959 client,
960 format!("/api/v8/core/submissions/{submission_id}/download"),
961 )?;
962 download(client, url, target)
963 }
964
965 pub fn post_submission_feedback(
968 client: &TestMyCodeClient,
969 submission_id: u32,
970 feedback: Vec<FeedbackAnswer>,
971 ) -> Result<SubmissionFeedbackResponse, TestMyCodeClientError> {
972 let url = make_url(
973 client,
974 format!("/api/v8/core/submissions/{submission_id}/feedback"),
975 )?;
976
977 let form = prepare_feedback_form(feedback);
978 let res = prepare_tmc_request(client, Method::POST, url.clone())
979 .form(&form)
980 .send()
981 .map_err(|e| TestMyCodeClientError::ConnectionError(Method::POST, url.clone(), e))?;
982
983 let json = assert_success_json(res, &url)?;
984 Ok(json)
985 }
986
987 pub fn post_submission_review(
990 client: &TestMyCodeClient,
991 submission_id: u32,
992 review: String,
993 ) -> Result<(), TestMyCodeClientError> {
995 let url = make_url(
996 client,
997 format!("/api/v8/core/submissions/{submission_id}/reviews"),
998 )?;
999
1000 let mut form = HashMap::new();
1001 form.insert("review[review_body]".to_string(), review);
1002
1003 let res = prepare_tmc_request(client, Method::POST, url.clone())
1010 .form(&form)
1011 .send()
1012 .map_err(|e| TestMyCodeClientError::ConnectionError(Method::POST, url.clone(), e))?;
1013
1014 assert_success(res, &url)?;
1015 Ok(())
1016 }
1017}
1018
1019#[cfg(test)]
1020#[allow(clippy::unwrap_used)]
1021mod test {
1022 use super::{super::TestMyCodeClient, *};
1023 use mockito::{Matcher, Mock, Server};
1024 use std::io::{Cursor, Seek};
1025
1026 fn init() {
1027 use log::*;
1028 use simple_logger::*;
1029 let _ = SimpleLogger::new().with_level(LevelFilter::Debug).init();
1030 }
1031
1032 fn make_client(server: &Server) -> TestMyCodeClient {
1033 TestMyCodeClient::new(
1034 server.url().parse().unwrap(),
1035 "client".to_string(),
1036 "version".to_string(),
1037 )
1038 .unwrap()
1039 }
1040
1041 fn client_matcher() -> Matcher {
1042 Matcher::AllOf(vec![
1043 Matcher::UrlEncoded("client".to_string(), "client".to_string()),
1044 Matcher::UrlEncoded("client_version".to_string(), "version".to_string()),
1045 ])
1046 }
1047
1048 fn mock_get(server: &mut Server, path: &str, body: &str) -> Mock {
1049 server
1050 .mock("GET", path)
1051 .match_query(client_matcher())
1052 .with_body(body)
1053 .create()
1054 }
1055
1056 #[test]
1057 fn gets_credentials() {
1058 init();
1059 let mut server = Server::new();
1060
1061 let client = make_client(&server);
1062 let _m = mock_get(
1063 &mut server,
1064 "/api/v8/application/client/credentials",
1065 r#"
1066 {
1067 "application_id": "id",
1068 "secret": "s"
1069 }
1070 "#,
1071 );
1072
1073 let _res = get_credentials(&client).unwrap();
1074 }
1075
1076 #[test]
1077 fn gets_submission_processing_status() {
1078 init();
1079 let mut server = Server::new();
1080
1081 let client = make_client(&server);
1082 let _m = server.mock("GET", "/api/v8/core/submissions/0")
1083 .match_query(Matcher::AllOf(vec![
1084 Matcher::UrlEncoded("client".into(), "client".into()),
1085 Matcher::UrlEncoded("client_version".into(), "version".into()),
1086 ]))
1087 .with_body(serde_json::json!({
1088 "api_version": 7,
1089 "all_tests_passed": true,
1090 "user_id": 3232,
1091 "login": "014464865",
1092 "course": "mooc-java-programming-i",
1093 "exercise_name": "part01-Part01_01.Sandbox",
1094 "status": "ok",
1095 "points": [
1096 "01-01"
1097 ],
1098 "validations": {
1099 "strategy": "DISABLED",
1100 "validationErrors": {}
1101 },
1102 "valgrind": "",
1103 "submission_url": "https://tmc.mooc.fi/submissions/7402793",
1104 "solution_url": "https://tmc.mooc.fi/exercises/83113/solution",
1105 "submitted_at": "2020-06-15T16:05:08.105+03:00",
1106 "processing_time": 13,
1107 "reviewed": false,
1108 "requests_review": true,
1109 "paste_url": null,
1110 "message_for_paste": null,
1111 "missing_review_points": [],
1112 "test_cases": [
1113 {
1114 "name": "SandboxTest freePoints",
1115 "successful": true,
1116 "message": "",
1117 "exception": [],
1118 "detailed_message": null
1119 }
1120 ],
1121 "feedback_questions": [
1122 {
1123 "id": 389,
1124 "question": "How well did you concentrate doing this exercise? (1: not at all, 5: very well)",
1125 "kind": "intrange[1..5]"
1126 },
1127 {
1128 "id": 390,
1129 "question": "How much do you feel you learned doing this exercise? (1: Did not learn anything, 5: Learned a lot)",
1130 "kind": "intrange[1..5]"
1131 },
1132 ],
1133 "feedback_answer_url": "https://tmc.mooc.fi/api/v8/core/submissions/7402793/feedback"
1134 }).to_string()).create();
1135
1136 let submission_processing_status = get_submission(&client, 0).unwrap();
1137 match submission_processing_status {
1138 SubmissionProcessingStatus::Finished(f) => {
1139 assert_eq!(f.all_tests_passed, Some(true));
1140 }
1141 SubmissionProcessingStatus::Processing(_) => panic!("wrong status"),
1142 }
1143 }
1144
1145 #[test]
1146 fn user_get() {
1147 init();
1148 let mut server = Server::new();
1149
1150 let client = &make_client(&server);
1151 let _m = mock_get(
1152 &mut server,
1153 "/api/v8/users/0",
1154 r#"
1155{
1156 "id": 1,
1157 "username": "student",
1158 "email": "student@example.com",
1159 "administrator": false
1160}
1161"#,
1162 );
1163 let _user = user::get(client, 0).unwrap();
1164 }
1165
1166 #[test]
1167 fn user_get_current() {
1168 init();
1169 let mut server = Server::new();
1170
1171 let client = &make_client(&server);
1172 let _m = mock_get(
1173 &mut server,
1174 "/api/v8/users/current",
1175 r#"
1176{
1177 "id": 1,
1178 "username": "student",
1179 "email": "student@example.com",
1180 "administrator": false
1181}
1182"#,
1183 );
1184
1185 let _res = user::get_current(client).unwrap();
1186 }
1187
1188 #[test]
1189 fn user_get_basic_info_by_usernames() {
1190 init();
1191 let mut server = Server::new();
1192
1193 let client = &make_client(&server);
1194 let _m = server
1195 .mock("POST", "/api/v8/users/basic_info_by_usernames")
1196 .match_query(client_matcher())
1197 .match_body(Matcher::JsonString(
1198 r#"{"usernames": ["username"]}"#.to_string(),
1199 ))
1200 .with_body(
1201 r#"
1202[
1203 {
1204 "id": 1,
1205 "username": "student",
1206 "email": "student@example.com",
1207 "administrator": false
1208 }
1209]
1210"#,
1211 )
1212 .create();
1213
1214 let _res = user::get_basic_info_by_usernames(client, &["username".to_string()]).unwrap();
1215 }
1216
1217 #[test]
1218 fn user_get_basic_info_by_emails() {
1219 init();
1220 let mut server = Server::new();
1221
1222 let client = &make_client(&server);
1223 let _m = server
1224 .mock("POST", "/api/v8/users/basic_info_by_emails")
1225 .match_query(client_matcher())
1226 .match_body(Matcher::JsonString(r#"{"emails": ["email"]}"#.to_string()))
1227 .with_body(
1228 r#"
1229[
1230 {
1231 "id": 1,
1232 "username": "student",
1233 "email": "student@example.com",
1234 "administrator": false
1235 }
1236]
1237"#,
1238 )
1239 .create();
1240
1241 let _res = user::get_basic_info_by_emails(client, &["email".to_string()]).unwrap();
1242 }
1243
1244 #[test]
1245 fn course_get_by_id() {
1246 init();
1247 let mut server = Server::new();
1248
1249 let client = &make_client(&server);
1250 let _m = mock_get(
1251 &mut server,
1252 "/api/v8/courses/0",
1253 r#"
1254{
1255 "name": "organizationid-coursename",
1256 "hide_after": "2016-10-10T13:22:19.554+03:00",
1257 "hidden": false,
1258 "cache_version": 1,
1259 "spreadsheet_key": "string",
1260 "hidden_if_registered_after": "string",
1261 "refreshed_at": "2016-10-10T13:22:36.871+03:00",
1262 "locked_exercise_points_visible": true,
1263 "description": "",
1264 "paste_visibility": 0,
1265 "formal_name": "string",
1266 "certificate_downloadable": false,
1267 "certificate_unlock_spec": "string",
1268 "organization_id": 1,
1269 "disabled_status": "enabled",
1270 "title": "testcourse",
1271 "material_url": "",
1272 "course_template_id": 1,
1273 "hide_submission_results": false,
1274 "external_scoreboard_url": "string",
1275 "organization_slug": "hy"
1276}
1277"#,
1278 );
1279
1280 let _res = course::get_by_id(client, 0).unwrap();
1281 }
1282
1283 #[test]
1284 fn course_get() {
1285 init();
1286 let mut server = Server::new();
1287
1288 let client = &make_client(&server);
1289 let _m = mock_get(
1290 &mut server,
1291 "/api/v8/org/someorg/courses/somecourse",
1292 r#"
1293{
1294 "name": "organizationid-coursename",
1295 "hide_after": "2016-10-10T13:22:19.554+03:00",
1296 "hidden": false,
1297 "cache_version": 1,
1298 "spreadsheet_key": "string",
1299 "hidden_if_registered_after": "string",
1300 "refreshed_at": "2016-10-10T13:22:36.871+03:00",
1301 "locked_exercise_points_visible": true,
1302 "description": "",
1303 "paste_visibility": 0,
1304 "formal_name": "string",
1305 "certificate_downloadable": false,
1306 "certificate_unlock_spec": "string",
1307 "organization_id": 1,
1308 "disabled_status": "enabled",
1309 "title": "testcourse",
1310 "material_url": "",
1311 "course_template_id": 1,
1312 "hide_submission_results": false,
1313 "external_scoreboard_url": "string",
1314 "organization_slug": "hy"
1315}
1316"#,
1317 );
1318
1319 let _res = course::get(client, "someorg", "somecourse").unwrap();
1320 }
1321
1322 #[test]
1323 fn point_get_course_points_by_id() {
1324 init();
1325 let mut server = Server::new();
1326
1327 let client = &make_client(&server);
1328 let _m = mock_get(
1329 &mut server,
1330 "/api/v8/courses/0/points",
1331 r#"
1332[
1333 {
1334 "awarded_point": {
1335 "id": 1,
1336 "course_id": 1,
1337 "user_id": 1,
1338 "submission_id": 2,
1339 "name": "point name",
1340 "created_at": "2016-10-17T11:10:17.295+03:00"
1341 },
1342 "exercise_id": 1
1343 }
1344]
1345"#,
1346 );
1347
1348 let _res = point::get_course_points_by_id(client, 0).unwrap();
1349 }
1350
1351 #[test]
1352 fn point_get_exercise_points_by_id() {
1353 init();
1354 let mut server = Server::new();
1355
1356 let client = &make_client(&server);
1357 let _m = mock_get(
1358 &mut server,
1359 "/api/v8/courses/0/exercises/someexercise/points",
1360 r#"
1361[
1362 {
1363 "awarded_point": {
1364 "id": 1,
1365 "course_id": 1,
1366 "user_id": 1,
1367 "submission_id": 2,
1368 "name": "point name",
1369 "created_at": "2016-10-17T11:10:17.295+03:00"
1370 },
1371 "exercise_id": 1
1372 }
1373]
1374"#,
1375 );
1376
1377 let _res = point::get_exercise_points_by_id(client, 0, "someexercise").unwrap();
1378 }
1379
1380 #[test]
1381 fn point_get_exercise_points_for_user_by_id() {
1382 init();
1383 let mut server = Server::new();
1384
1385 let client = &make_client(&server);
1386 let _m = mock_get(
1387 &mut server,
1388 "/api/v8/courses/0/exercises/someexercise/users/1/points",
1389 r#"
1390[
1391 {
1392 "awarded_point": {
1393 "id": 1,
1394 "course_id": 1,
1395 "user_id": 1,
1396 "submission_id": 2,
1397 "name": "point name",
1398 "created_at": "2016-10-17T11:10:17.295+03:00"
1399 },
1400 "exercise_id": 1
1401 }
1402]
1403"#,
1404 );
1405
1406 let _res = point::get_exercise_points_for_user_by_id(client, 0, "someexercise", 1).unwrap();
1407 }
1408
1409 #[test]
1410 fn point_get_exercise_points_for_current_user_by_id() {
1411 init();
1412 let mut server = Server::new();
1413
1414 let client = &make_client(&server);
1415 let _m = mock_get(
1416 &mut server,
1417 "/api/v8/courses/0/exercises/someexercise/users/current/points",
1418 r#"
1419[
1420 {
1421 "awarded_point": {
1422 "id": 1,
1423 "course_id": 1,
1424 "user_id": 1,
1425 "submission_id": 2,
1426 "name": "point name",
1427 "created_at": "2016-10-17T11:10:17.295+03:00"
1428 },
1429 "exercise_id": 1
1430 }
1431]
1432"#,
1433 );
1434
1435 let _res =
1436 point::get_exercise_points_for_current_user_by_id(client, 0, "someexercise").unwrap();
1437 }
1438
1439 #[test]
1440 fn point_get_course_points_for_user_by_id() {
1441 init();
1442 let mut server = Server::new();
1443
1444 let client = &make_client(&server);
1445 let _m = mock_get(
1446 &mut server,
1447 "/api/v8/courses/0/users/1/points",
1448 r#"
1449[
1450 {
1451 "awarded_point": {
1452 "id": 1,
1453 "course_id": 1,
1454 "user_id": 1,
1455 "submission_id": 2,
1456 "name": "point name",
1457 "created_at": "2016-10-17T11:10:17.295+03:00"
1458 },
1459 "exercise_id": 1
1460 }
1461]
1462"#,
1463 );
1464
1465 let _res = point::get_course_points_for_user_by_id(client, 0, 1).unwrap();
1466 }
1467
1468 #[test]
1469 fn point_get_course_points_for_current_user_by_id() {
1470 init();
1471 let mut server = Server::new();
1472
1473 let client = &make_client(&server);
1474 let _m = mock_get(
1475 &mut server,
1476 "/api/v8/courses/0/users/current/points",
1477 r#"
1478[
1479 {
1480 "awarded_point": {
1481 "id": 1,
1482 "course_id": 1,
1483 "user_id": 1,
1484 "submission_id": 2,
1485 "name": "point name",
1486 "created_at": "2016-10-17T11:10:17.295+03:00"
1487 },
1488 "exercise_id": 1
1489 }
1490]
1491"#,
1492 );
1493
1494 let _res = point::get_course_points_for_current_user_by_id(client, 0).unwrap();
1495 }
1496
1497 #[test]
1498 fn point_get_course_points() {
1499 init();
1500 let mut server = Server::new();
1501
1502 let client = &make_client(&server);
1503 let _m = mock_get(
1504 &mut server,
1505 "/api/v8/org/someorg/courses/somecourse/points",
1506 r#"
1507[
1508 {
1509 "awarded_point": {
1510 "id": 1,
1511 "course_id": 1,
1512 "user_id": 1,
1513 "submission_id": 2,
1514 "name": "point name",
1515 "created_at": "2016-10-17T11:10:17.295+03:00"
1516 },
1517 "exercise_id": 1
1518 }
1519]
1520"#,
1521 );
1522
1523 let _res = point::get_course_points(client, "someorg", "somecourse").unwrap();
1524 }
1525
1526 #[test]
1527 fn point_get_exercise_points() {
1528 init();
1529 let mut server = Server::new();
1530
1531 let client = &make_client(&server);
1532 let _m = mock_get(
1533 &mut server,
1534 "/api/v8/org/someorg/courses/somecourse/exercises/someexercise/points",
1535 r#"
1536[
1537 {
1538 "awarded_point": {
1539 "id": 1,
1540 "course_id": 1,
1541 "user_id": 1,
1542 "submission_id": 2,
1543 "name": "point name",
1544 "created_at": "2016-10-17T11:10:17.295+03:00"
1545 },
1546 "exercise_id": 1
1547 }
1548]
1549"#,
1550 );
1551
1552 let _res =
1553 point::get_exercise_points(client, "someorg", "somecourse", "someexercise").unwrap();
1554 }
1555
1556 #[test]
1557 fn point_get_course_points_for_user() {
1558 init();
1559 let mut server = Server::new();
1560
1561 let client = &make_client(&server);
1562 let _m = mock_get(
1563 &mut server,
1564 "/api/v8/org/someorg/courses/somecourse/users/0/points",
1565 r#"
1566[
1567 {
1568 "awarded_point": {
1569 "id": 1,
1570 "course_id": 1,
1571 "user_id": 1,
1572 "submission_id": 2,
1573 "name": "point name",
1574 "created_at": "2016-10-17T11:10:17.295+03:00"
1575 },
1576 "exercise_id": 1
1577 }
1578]
1579 "#,
1580 );
1581
1582 let _res = point::get_course_points_for_user(client, "someorg", "somecourse", 0).unwrap();
1583 }
1584
1585 #[test]
1586 fn point_get_course_points_for_current_user() {
1587 init();
1588 let mut server = Server::new();
1589
1590 let client = &make_client(&server);
1591 let _m = mock_get(
1592 &mut server,
1593 "/api/v8/org/someorg/courses/somecourse/users/current/points",
1594 r#"
1595[
1596 {
1597 "awarded_point": {
1598 "id": 1,
1599 "course_id": 1,
1600 "user_id": 1,
1601 "submission_id": 2,
1602 "name": "point name",
1603 "created_at": "2016-10-17T11:10:17.295+03:00"
1604 },
1605 "exercise_id": 1
1606 }
1607]
1608 "#,
1609 );
1610
1611 let _res =
1612 point::get_course_points_for_current_user(client, "someorg", "somecourse").unwrap();
1613 }
1614
1615 #[test]
1616 fn submission_get_course_submissions_by_id() {
1617 init();
1618 let mut server = Server::new();
1619
1620 let client = &make_client(&server);
1621 let _m = mock_get(
1622 &mut server,
1623 "/api/v8/courses/0/submissions",
1624 r#"
1625[
1626 {
1627 "id": 1,
1628 "user_id": 1,
1629 "pretest_error": "Missing test output. Did you terminate your program with an exit() command?",
1630 "created_at": "2016-10-17T11:10:17.295+03:00",
1631 "exercise_name": "trivial",
1632 "course_id": 1,
1633 "processed": true,
1634 "all_tests_passed": true,
1635 "points": "string",
1636 "processing_tried_at": "2016-10-17T11:10:17.295+03:00",
1637 "processing_began_at": "2016-10-17T11:10:17.295+03:00",
1638 "processing_completed_at": "2016-10-17T11:10:17.295+03:00",
1639 "times_sent_to_sandbox": 1,
1640 "processing_attempts_started_at": "2016-10-17T11:10:17.295+03:00",
1641 "params_json": "{\"error_msg_locale\":\"en\"}",
1642 "requires_review": true,
1643 "requests_review": true,
1644 "reviewed": true,
1645 "message_for_reviewer": "",
1646 "newer_submission_reviewed": true,
1647 "review_dismissed": true,
1648 "paste_available": true,
1649 "message_for_paste": "",
1650 "paste_key": "string"
1651 }
1652]
1653"#,
1654 );
1655
1656 let _res = submission::get_course_submissions_by_id(client, 0).unwrap();
1657 }
1658
1659 #[test]
1660 fn submission_get_course_submissions_for_last_hour() {
1661 init();
1662 let mut server = Server::new();
1663
1664 let client = &make_client(&server);
1665 let _m = mock_get(
1666 &mut server,
1667 "/api/v8/courses/0/submissions/last_hour",
1668 r#"
1669[
1670 1
1671]
1672"#,
1673 );
1674
1675 let _res = submission::get_course_submissions_for_last_hour(client, 0).unwrap();
1676 }
1677
1678 #[test]
1679 fn submission_get_course_submissions_for_user_by_id() {
1680 init();
1681 let mut server = Server::new();
1682
1683 let client = &make_client(&server);
1684 let _m = server.mock("GET", "/api/v8/courses/0/users/1/submissions")
1685 .match_query(client_matcher())
1686 .with_body(
1687 r#"
1688[
1689 {
1690 "id": 1,
1691 "user_id": 1,
1692 "pretest_error": "Missing test output. Did you terminate your program with an exit() command?",
1693 "created_at": "2016-10-17T11:10:17.295+03:00",
1694 "exercise_name": "trivial",
1695 "course_id": 1,
1696 "processed": true,
1697 "all_tests_passed": true,
1698 "points": "string",
1699 "processing_tried_at": "2016-10-17T11:10:17.295+03:00",
1700 "processing_began_at": "2016-10-17T11:10:17.295+03:00",
1701 "processing_completed_at": "2016-10-17T11:10:17.295+03:00",
1702 "times_sent_to_sandbox": 1,
1703 "processing_attempts_started_at": "2016-10-17T11:10:17.295+03:00",
1704 "params_json": "{\"error_msg_locale\":\"en\"}",
1705 "requires_review": true,
1706 "requests_review": true,
1707 "reviewed": true,
1708 "message_for_reviewer": "",
1709 "newer_submission_reviewed": true,
1710 "review_dismissed": true,
1711 "paste_available": true,
1712 "message_for_paste": "",
1713 "paste_key": "string"
1714 }
1715]
1716"#,
1717 )
1718 .create();
1719
1720 let _res = submission::get_course_submissions_for_user_by_id(client, 0, 1).unwrap();
1721 }
1722
1723 #[test]
1724 fn submission_get_course_submissions_for_current_user_by_id() {
1725 init();
1726 let mut server = Server::new();
1727
1728 let client = &make_client(&server);
1729 let _m = mock_get(
1730 &mut server,
1731 "/api/v8/courses/0/users/current/submissions",
1732 r#"
1733[
1734 {
1735 "id": 1,
1736 "user_id": 1,
1737 "pretest_error": "Missing test output. Did you terminate your program with an exit() command?",
1738 "created_at": "2016-10-17T11:10:17.295+03:00",
1739 "exercise_name": "trivial",
1740 "course_id": 1,
1741 "processed": true,
1742 "all_tests_passed": true,
1743 "points": "string",
1744 "processing_tried_at": "2016-10-17T11:10:17.295+03:00",
1745 "processing_began_at": "2016-10-17T11:10:17.295+03:00",
1746 "processing_completed_at": "2016-10-17T11:10:17.295+03:00",
1747 "times_sent_to_sandbox": 1,
1748 "processing_attempts_started_at": "2016-10-17T11:10:17.295+03:00",
1749 "params_json": "{\"error_msg_locale\":\"en\"}",
1750 "requires_review": true,
1751 "requests_review": true,
1752 "reviewed": true,
1753 "message_for_reviewer": "",
1754 "newer_submission_reviewed": true,
1755 "review_dismissed": true,
1756 "paste_available": true,
1757 "message_for_paste": "",
1758 "paste_key": "string"
1759 }
1760]
1761"#,
1762 );
1763
1764 let _res = submission::get_course_submissions_for_current_user_by_id(client, 0).unwrap();
1765 }
1766
1767 #[test]
1768 fn submission_get_exercise_submissions_for_user() {
1769 init();
1770 let mut server = Server::new();
1771
1772 let client = &make_client(&server);
1773 let _m = mock_get(
1774 &mut server,
1775 "/api/v8/org/someorg/courses/somecourse/users/0/submissions",
1776 r#"
1777[
1778 {
1779 "id": 1,
1780 "user_id": 1,
1781 "pretest_error": "Missing test output. Did you terminate your program with an exit() command?",
1782 "created_at": "2016-10-17T11:10:17.295+03:00",
1783 "exercise_name": "trivial",
1784 "course_id": 1,
1785 "processed": true,
1786 "all_tests_passed": true,
1787 "points": "string",
1788 "processing_tried_at": "2016-10-17T11:10:17.295+03:00",
1789 "processing_began_at": "2016-10-17T11:10:17.295+03:00",
1790 "processing_completed_at": "2016-10-17T11:10:17.295+03:00",
1791 "times_sent_to_sandbox": 1,
1792 "processing_attempts_started_at": "2016-10-17T11:10:17.295+03:00",
1793 "params_json": "{\"error_msg_locale\":\"en\"}",
1794 "requires_review": true,
1795 "requests_review": true,
1796 "reviewed": true,
1797 "message_for_reviewer": "",
1798 "newer_submission_reviewed": true,
1799 "review_dismissed": true,
1800 "paste_available": true,
1801 "message_for_paste": "",
1802 "paste_key": "string"
1803 }
1804]
1805"#,
1806 );
1807
1808 let _res = submission::get_course_submissions_for_user(client, "someorg", "somecourse", 0)
1809 .unwrap();
1810 }
1811
1812 #[test]
1813 fn submission_get_exercise_submissions_for_current_user() {
1814 init();
1815 let mut server = Server::new();
1816
1817 let client = &make_client(&server);
1818 let _m = mock_get(
1819 &mut server,
1820 "/api/v8/org/someorg/courses/somecourse/users/current/submissions",
1821 r#"
1822[
1823 {
1824 "id": 1,
1825 "user_id": 1,
1826 "pretest_error": "Missing test output. Did you terminate your program with an exit() command?",
1827 "created_at": "2016-10-17T11:10:17.295+03:00",
1828 "exercise_name": "trivial",
1829 "course_id": 1,
1830 "processed": true,
1831 "all_tests_passed": true,
1832 "points": "string",
1833 "processing_tried_at": "2016-10-17T11:10:17.295+03:00",
1834 "processing_began_at": "2016-10-17T11:10:17.295+03:00",
1835 "processing_completed_at": "2016-10-17T11:10:17.295+03:00",
1836 "times_sent_to_sandbox": 1,
1837 "processing_attempts_started_at": "2016-10-17T11:10:17.295+03:00",
1838 "params_json": "{\"error_msg_locale\":\"en\"}",
1839 "requires_review": true,
1840 "requests_review": true,
1841 "reviewed": true,
1842 "message_for_reviewer": "",
1843 "newer_submission_reviewed": true,
1844 "review_dismissed": true,
1845 "paste_available": true,
1846 "message_for_paste": "",
1847 "paste_key": "string"
1848 }
1849]
1850"#,
1851 );
1852
1853 let _res =
1854 submission::get_course_submissions_for_current_user(client, "someorg", "somecourse")
1855 .unwrap();
1856 }
1857
1858 #[test]
1859 fn submission_get_course_submissions() {
1860 init();
1861 let mut server = Server::new();
1862
1863 let client = &make_client(&server);
1864 let _m = mock_get(
1865 &mut server,
1866 "/api/v8/org/someorg/courses/somecourse/submissions",
1867 r#"
1868[
1869 {
1870 "id": 1,
1871 "user_id": 1,
1872 "pretest_error": "Missing test output. Did you terminate your program with an exit() command?",
1873 "created_at": "2016-10-17T11:10:17.295+03:00",
1874 "exercise_name": "trivial",
1875 "course_id": 1,
1876 "processed": true,
1877 "all_tests_passed": true,
1878 "points": "string",
1879 "processing_tried_at": "2016-10-17T11:10:17.295+03:00",
1880 "processing_began_at": "2016-10-17T11:10:17.295+03:00",
1881 "processing_completed_at": "2016-10-17T11:10:17.295+03:00",
1882 "times_sent_to_sandbox": 1,
1883 "processing_attempts_started_at": "2016-10-17T11:10:17.295+03:00",
1884 "params_json": "{\"error_msg_locale\":\"en\"}",
1885 "requires_review": true,
1886 "requests_review": true,
1887 "reviewed": true,
1888 "message_for_reviewer": "",
1889 "newer_submission_reviewed": true,
1890 "review_dismissed": true,
1891 "paste_available": true,
1892 "message_for_paste": "",
1893 "paste_key": "string"
1894 }
1895]
1896"#,
1897 );
1898
1899 let _res = submission::get_course_submissions(client, "someorg", "somecourse").unwrap();
1900 }
1901
1902 #[test]
1903 fn submission_get_course_submissions_for_user() {
1904 init();
1905 let mut server = Server::new();
1906
1907 let client = &make_client(&server);
1908 let _m = mock_get(
1909 &mut server,
1910 "/api/v8/org/someorg/courses/somecourse/users/0/submissions",
1911 r#"
1912[
1913 {
1914 "id": 1,
1915 "user_id": 1,
1916 "pretest_error": "Missing test output. Did you terminate your program with an exit() command?",
1917 "created_at": "2016-10-17T11:10:17.295+03:00",
1918 "exercise_name": "trivial",
1919 "course_id": 1,
1920 "processed": true,
1921 "all_tests_passed": true,
1922 "points": "string",
1923 "processing_tried_at": "2016-10-17T11:10:17.295+03:00",
1924 "processing_began_at": "2016-10-17T11:10:17.295+03:00",
1925 "processing_completed_at": "2016-10-17T11:10:17.295+03:00",
1926 "times_sent_to_sandbox": 1,
1927 "processing_attempts_started_at": "2016-10-17T11:10:17.295+03:00",
1928 "params_json": "{\"error_msg_locale\":\"en\"}",
1929 "requires_review": true,
1930 "requests_review": true,
1931 "reviewed": true,
1932 "message_for_reviewer": "",
1933 "newer_submission_reviewed": true,
1934 "review_dismissed": true,
1935 "paste_available": true,
1936 "message_for_paste": "",
1937 "paste_key": "string"
1938 }
1939]
1940"#,
1941 );
1942
1943 let _res = submission::get_course_submissions_for_user(client, "someorg", "somecourse", 0)
1944 .unwrap();
1945 }
1946
1947 #[test]
1948 fn submission_get_course_submissions_for_current_user() {
1949 init();
1950 let mut server = Server::new();
1951
1952 let client = &make_client(&server);
1953 let _m = mock_get(
1954 &mut server,
1955 "/api/v8/org/someorg/courses/somecourse/users/current/submissions",
1956 r#"
1957[
1958 {
1959 "id": 1,
1960 "user_id": 1,
1961 "pretest_error": "Missing test output. Did you terminate your program with an exit() command?",
1962 "created_at": "2016-10-17T11:10:17.295+03:00",
1963 "exercise_name": "trivial",
1964 "course_id": 1,
1965 "processed": true,
1966 "all_tests_passed": true,
1967 "points": "string",
1968 "processing_tried_at": "2016-10-17T11:10:17.295+03:00",
1969 "processing_began_at": "2016-10-17T11:10:17.295+03:00",
1970 "processing_completed_at": "2016-10-17T11:10:17.295+03:00",
1971 "times_sent_to_sandbox": 1,
1972 "processing_attempts_started_at": "2016-10-17T11:10:17.295+03:00",
1973 "params_json": "{\"error_msg_locale\":\"en\"}",
1974 "requires_review": true,
1975 "requests_review": true,
1976 "reviewed": true,
1977 "message_for_reviewer": "",
1978 "newer_submission_reviewed": true,
1979 "review_dismissed": true,
1980 "paste_available": true,
1981 "message_for_paste": "",
1982 "paste_key": "string"
1983 }
1984]
1985"#,
1986 );
1987
1988 let _res =
1989 submission::get_course_submissions_for_current_user(client, "someorg", "somecourse")
1990 .unwrap();
1991 }
1992
1993 #[test]
1994 fn exercise_get_course_exercises_by_id() {
1995 init();
1996 let mut server = Server::new();
1997
1998 let client = &make_client(&server);
1999 let _m = mock_get(
2000 &mut server,
2001 "/api/v8/courses/0/exercises",
2002 r#"
2003[
2004 {
2005 "id": 1,
2006 "name": "Exercise name",
2007 "publish_time": "2016-10-24T14:06:36.730+03:00",
2008 "solution_visible_after": "2016-10-24T14:06:36.730+03:00",
2009 "deadline": "2016-10-24T14:06:36.730+03:00",
2010 "soft_deadline": "2016-10-24T14:06:36.730+03:00",
2011 "disabled": false,
2012 "awarded_points": [],
2013 "available_points": [
2014 {
2015 "id": 1,
2016 "exercise_id": 1,
2017 "name": "Point name",
2018 "requires_review": false
2019 }
2020 ],
2021 "unlocked": false
2022 }
2023]
2024"#,
2025 );
2026
2027 let _res = exercise::get_course_exercises_by_id(client, 0).unwrap();
2028 }
2029
2030 #[test]
2031 fn exercise_get_exercise_submissions_for_user() {
2032 init();
2033 let mut server = Server::new();
2034
2035 let client = &make_client(&server);
2036 let _m = mock_get(
2037 &mut server,
2038 "/api/v8/exercises/0/users/1/submissions",
2039 r#"
2040[
2041 {
2042 "id": 1,
2043 "user_id": 1,
2044 "pretest_error": "Missing test output. Did you terminate your program with an exit() command?",
2045 "created_at": "2016-10-17T11:10:17.295+03:00",
2046 "exercise_name": "trivial",
2047 "course_id": 1,
2048 "processed": true,
2049 "all_tests_passed": true,
2050 "points": "string",
2051 "processing_tried_at": "2016-10-17T11:10:17.295+03:00",
2052 "processing_began_at": "2016-10-17T11:10:17.295+03:00",
2053 "processing_completed_at": "2016-10-17T11:10:17.295+03:00",
2054 "times_sent_to_sandbox": 1,
2055 "processing_attempts_started_at": "2016-10-17T11:10:17.295+03:00",
2056 "params_json": "{\"error_msg_locale\":\"en\"}",
2057 "requires_review": true,
2058 "requests_review": true,
2059 "reviewed": true,
2060 "message_for_reviewer": "",
2061 "newer_submission_reviewed": true,
2062 "review_dismissed": true,
2063 "paste_available": true,
2064 "message_for_paste": "",
2065 "paste_key": "string"
2066 }
2067]
2068"#,
2069 );
2070
2071 let _res = exercise::get_exercise_submissions_for_user(client, 0, 1).unwrap();
2072 }
2073
2074 #[test]
2075 fn exercise_get_exercise_submissions_for_current_user() {
2076 init();
2077 let mut server = Server::new();
2078
2079 let client = &make_client(&server);
2080 let _m = mock_get(
2081 &mut server,
2082 "/api/v8/exercises/0/users/current/submissions",
2083 r#"
2084[
2085 {
2086 "id": 1,
2087 "user_id": 1,
2088 "pretest_error": "Missing test output. Did you terminate your program with an exit() command?",
2089 "created_at": "2016-10-17T11:10:17.295+03:00",
2090 "exercise_name": "trivial",
2091 "course_id": 1,
2092 "processed": true,
2093 "all_tests_passed": true,
2094 "points": "string",
2095 "processing_tried_at": "2016-10-17T11:10:17.295+03:00",
2096 "processing_began_at": "2016-10-17T11:10:17.295+03:00",
2097 "processing_completed_at": "2016-10-17T11:10:17.295+03:00",
2098 "times_sent_to_sandbox": 1,
2099 "processing_attempts_started_at": "2016-10-17T11:10:17.295+03:00",
2100 "params_json": "{\"error_msg_locale\":\"en\"}",
2101 "requires_review": true,
2102 "requests_review": true,
2103 "reviewed": true,
2104 "message_for_reviewer": "",
2105 "newer_submission_reviewed": true,
2106 "review_dismissed": true,
2107 "paste_available": true,
2108 "message_for_paste": "",
2109 "paste_key": "string"
2110 }
2111]
2112"#,
2113 );
2114
2115 let _res = exercise::get_exercise_submissions_for_current_user(client, 0).unwrap();
2116 }
2117
2118 #[test]
2119 fn exercise_get_course_exercises() {
2120 init();
2121 let mut server = Server::new();
2122
2123 let client = &make_client(&server);
2124 let _m = mock_get(
2125 &mut server,
2126 "/api/v8/org/someorg/courses/somecourse/exercises",
2127 r#"
2128[
2129 {
2130 "id": 1,
2131 "name": "Exercise name",
2132 "publish_time": "2016-10-24T14:06:36.730+03:00",
2133 "solution_visible_after": "2016-10-24T14:06:36.730+03:00",
2134 "deadline": "2016-10-24T14:06:36.730+03:00",
2135 "soft_deadline": "2016-10-24T14:06:36.730+03:00",
2136 "disabled": false,
2137 "awarded_points": [],
2138 "available_points": [
2139 {
2140 "id": 1,
2141 "exercise_id": 1,
2142 "name": "Point name",
2143 "requires_review": false
2144 }
2145 ],
2146 "unlocked": false
2147 }
2148]
2149"#,
2150 );
2151
2152 let _res = exercise::get_course_exercises(client, "someorg", "somecourse").unwrap();
2153 }
2154
2155 #[test]
2156 fn exercise_download_course_exercise() {
2157 init();
2158 let mut server = Server::new();
2159
2160 let client = &make_client(&server);
2161 let _m = server
2162 .mock(
2163 "GET",
2164 "/api/v8/org/someorg/courses/somecourse/exercises/someexercise/download",
2165 )
2166 .match_query(client_matcher())
2167 .with_body(b"1234")
2168 .create();
2169
2170 let mut temp = tempfile::tempfile().unwrap();
2171 exercise::download_course_exercise(
2172 client,
2173 "someorg",
2174 "somecourse",
2175 "someexercise",
2176 &mut temp,
2177 )
2178 .unwrap();
2179 let mut buf = vec![];
2180 temp.rewind().unwrap();
2181 temp.read_to_end(&mut buf).unwrap();
2182 assert!(!buf.is_empty());
2183 }
2184
2185 #[test]
2186 fn organization_get_organizations() {
2187 init();
2188 let mut server = Server::new();
2189
2190 let client = &make_client(&server);
2191 let _m = mock_get(
2192 &mut server,
2193 "/api/v8/org.json",
2194 r#"
2195 [
2196 {
2197 "name": "University of Helsinki",
2198 "information": "Organization for University of Helsinki",
2199 "slug": "hy",
2200 "logo_path": "/logos/hy_logo.png",
2201 "pinned": false
2202 }
2203]
2204"#,
2205 );
2206
2207 let _res = organization::get_organizations(client).unwrap();
2208 }
2209
2210 #[test]
2211 fn organization_get_organization() {
2212 init();
2213 let mut server = Server::new();
2214
2215 let client = &make_client(&server);
2216 let _m = mock_get(
2217 &mut server,
2218 "/api/v8/org/someorg.json",
2219 r#"
2220 {
2221 "name": "University of Helsinki",
2222 "information": "Organization for University of Helsinki",
2223 "slug": "hy",
2224 "logo_path": "/logos/hy_logo.png",
2225 "pinned": false
2226}
2227"#,
2228 );
2229
2230 let _res = organization::get_organization(client, "someorg").unwrap();
2231 }
2232
2233 #[test]
2234 fn core_get_course() {
2235 init();
2236 let mut server = Server::new();
2237
2238 let client = &make_client(&server);
2239 let _m = mock_get(
2240 &mut server,
2241 "/api/v8/core/courses/0",
2242 r#"
2243{
2244 "course": {
2245 "id": 13,
2246 "name": "organizationid-coursename",
2247 "title": "coursetitle",
2248 "description": "description of the course",
2249 "details_url": "http://tmc.mooc.fi/api/v8/core/courses/13",
2250 "unlock_url": "https://tmc.mooc.fi/api/v8/core/courses/13/unlock",
2251 "reviews_url": "https://tmc.mooc.fi/api/v8/core/courses/13/reviews",
2252 "comet_url": "https://tmc.mooc.fi:8443/comet",
2253 "spyware_urls": [
2254 "http://mooc.spyware.testmycode.net/"
2255 ],
2256 "unlockables": [
2257 ""
2258 ],
2259 "exercises": [
2260 {
2261 "id": 1,
2262 "name": "Exercise name",
2263 "locked": false,
2264 "deadline_description": "2016-02-29 23:59:00 +0200",
2265 "deadline": "2016-02-29T23:59:00.000+02:00",
2266 "checksum": "f25e139769b2688e213938456959eeaf",
2267 "return_url": "https://tmc.mooc.fi/api/v8/core/exercises/1337/submissions",
2268 "zip_url": "https://tmc.mooc.fi/api/v8/core/exercises/4272/download",
2269 "returnable": true,
2270 "requires_review": false,
2271 "attempted": false,
2272 "completed": false,
2273 "reviewed": false,
2274 "all_review_points_given": true,
2275 "memory_limit": 1024,
2276 "runtime_params": [
2277 "-Xss64M"
2278 ],
2279 "valgrind_strategy": "fail",
2280 "code_review_requests_enabled": false,
2281 "run_tests_locally_action_enabled": true,
2282 "exercise_submissions_url": "https://tmc.mooc.fi/api/v8/core/exercises/1337/solution/download",
2283 "latest_submission_url": "https://tmc.mooc.fi/api/v8/core/exercises/1337",
2284 "latest_submission_id": 13337,
2285 "solution_zip_url": "http://tmc.mooc.fi/api/v8/core/submissions/1337/download"
2286 }
2287 ]
2288 }
2289}
2290 "#,
2291 );
2292
2293 let _res = core::get_course(client, 0).unwrap();
2294 }
2295
2296 #[test]
2297 fn core_get_course_reviews() {
2298 init();
2299 let mut server = Server::new();
2300
2301 let client = &make_client(&server);
2302 let _m = mock_get(
2303 &mut server,
2304 "/api/v8/core/courses/0/reviews",
2305 r#"
2306 [
2307 {
2308 "submission_id": 1,
2309 "exercise_name": "trivial",
2310 "id": 1,
2311 "marked_as_read": false,
2312 "reviewer_name": "hn",
2313 "review_body": "",
2314 "points": [
2315 "string"
2316 ],
2317 "points_not_awarded": [
2318 "string"
2319 ],
2320 "url": "http://localhost:3000/api/core/v8/submissions/1/reviews",
2321 "update_url": "http://localhost:3000/api/v8/core/courses/1/reviews/1",
2322 "created_at": "2016-10-10T13:22:19.554+03:00",
2323 "updated_at": "2016-10-10T13:22:19.554+03:00"
2324 }
2325 ]
2326 "#,
2327 );
2328
2329 let _res = core::get_course_reviews(client, 0).unwrap();
2330 }
2331
2332 #[test]
2333 fn core_update_course_review() {
2334 init();
2335 let mut server = Server::new();
2336
2337 let client = &make_client(&server);
2338 let _m = server
2339 .mock("PUT", "/api/v8/core/courses/0/reviews/1")
2340 .match_query(client_matcher())
2341 .match_body(Matcher::AllOf(vec![
2342 Matcher::UrlEncoded("review[review_body]".to_string(), "body".to_string()),
2343 Matcher::UrlEncoded("mark_as_read".to_string(), "true".to_string()),
2344 ]))
2345 .create();
2346
2347 core::update_course_review(client, 0, 1, Some("body".to_string()), Some(true)).unwrap();
2348 }
2349
2350 #[test]
2351 fn core_unlock_course() {
2352 init();
2353 let mut server = Server::new();
2354
2355 let client = &make_client(&server);
2356 let _m = server
2357 .mock("POST", "/api/v8/core/courses/0/unlock")
2358 .match_query(client_matcher())
2359 .create();
2360
2361 core::unlock_course(client, 0).unwrap();
2362 }
2363
2364 #[test]
2365 fn core_download_exercise() {
2366 init();
2367 let mut server = Server::new();
2368
2369 let client = &make_client(&server);
2370 let _m = server
2371 .mock("GET", "/api/v8/core/exercises/0/download")
2372 .match_query(client_matcher())
2373 .with_body(b"1234")
2374 .create();
2375
2376 let mut temp = tempfile::tempfile().unwrap();
2377 core::download_exercise(client, 0, &mut temp).unwrap();
2378 let mut buf = vec![];
2379 temp.rewind().unwrap();
2380 temp.read_to_end(&mut buf).unwrap();
2381 assert!(!buf.is_empty());
2382 }
2383
2384 #[test]
2385 fn core_get_exercise() {
2386 init();
2387 let mut server = Server::new();
2388
2389 let client = &make_client(&server);
2390 let _m = mock_get(
2391 &mut server,
2392 "/api/v8/core/exercises/0",
2393 r#"
2394 {
2395 "course_name": "course",
2396 "course_id": 1,
2397 "code_review_requests_enabled": true,
2398 "run_tests_locally_action_enabled": true,
2399 "exercise_name": "exercise",
2400 "exercise_id": 1,
2401 "unlocked_at": "2016-12-05T12:00:00.000+03:00",
2402 "deadline": "2016-12-24T00:00:00.000+03:00",
2403 "submissions": [
2404 {
2405 "exercise_name": "exercise",
2406 "id": 1,
2407 "user_id": 1,
2408 "course_id": 1,
2409 "created_at": "2016-12-05T12:00:00.000+03:00",
2410 "all_tests_passed": true,
2411 "points": "point1",
2412 "submitted_zip_url": "http://example.com/api/v8/core/submissions/1/download",
2413 "paste_url": "http://example.com/paste/qqbKk2Z7INqBH8cmaZ7i_A,",
2414 "processing_time": 25,
2415 "reviewed": false,
2416 "requests_review": false
2417 }
2418 ]
2419 }
2420 "#,
2421 );
2422
2423 let _res = core::get_exercise(client, 0).unwrap();
2424 }
2425
2426 #[test]
2427 fn core_get_exercise_details() {
2428 init();
2429 let mut server = Server::new();
2430
2431 let client = &make_client(&server);
2432 let _m = server
2433 .mock("GET", "/api/v8/core/exercises/details")
2434 .match_query(Matcher::AllOf(vec![
2435 client_matcher(),
2436 Matcher::UrlEncoded("ids".to_string(), "0,1".to_string()),
2437 ]))
2438 .with_body(
2439 r#"
2440{
2441 "exercises": [
2442 {
2443 "id": 1,
2444 "course_name": "course",
2445 "exercise_name": "exercise",
2446 "checksum": "f25e139769b2688e213938456959eeaf",
2447 "hide_submission_results": false
2448 }
2449 ]
2450}
2451 "#,
2452 )
2453 .create();
2454
2455 let _res = core::get_exercise_details(client, &[0, 1]).unwrap();
2456 }
2457
2458 #[test]
2459 fn core_download_exercise_solution() {
2460 init();
2461 let mut server = Server::new();
2462
2463 let client = &make_client(&server);
2464 let _m = server
2465 .mock("GET", "/api/v8/core/exercises/0/solution/download")
2466 .match_query(client_matcher())
2467 .with_body(b"1234")
2468 .create();
2469
2470 let mut temp = tempfile::tempfile().unwrap();
2471 core::download_exercise_solution(client, 0, &mut temp).unwrap();
2472 let mut buf = vec![];
2473 temp.rewind().unwrap();
2474 temp.read_to_end(&mut buf).unwrap();
2475 assert!(!buf.is_empty());
2476 }
2477
2478 #[test]
2479 fn core_submit_exercise() {
2480 init();
2481 let mut server = Server::new();
2482
2483 let client = &make_client(&server);
2484 let _m = server
2485 .mock("POST", "/api/v8/core/exercises/0/submissions")
2486 .match_query(client_matcher())
2487 .match_body(Matcher::AllOf(vec![
2488 Matcher::Regex("submission\\[file\\]".to_string()),
2489 Matcher::Regex("paste".to_string()),
2490 Matcher::Regex("message_for_paste".to_string()),
2491 Matcher::Regex("request_review".to_string()),
2492 Matcher::Regex("message_for_reviewer".to_string()),
2493 Matcher::Regex("error_msg_locale".to_string()),
2494 ]))
2495 .with_body(
2496 r#"
2497 {
2498 "show_submission_url": "someurl",
2499 "paste_url": "anotherurl",
2500 "submission_url": "third"
2501 }
2502 "#,
2503 )
2504 .create();
2505
2506 let _res = core::submit_exercise(
2507 client,
2508 0,
2509 Cursor::new(vec![]),
2510 Some(PasteData::WithMessage("paste".to_string())),
2511 Some(ReviewData::WithMessage("message".to_string())),
2512 Some(Language::from_639_1("fi").unwrap()),
2513 )
2514 .unwrap();
2515 }
2516
2517 #[test]
2518 fn core_get_organization_courses() {
2519 init();
2520 let mut server = Server::new();
2521
2522 let client = &make_client(&server);
2523 let _m = mock_get(
2524 &mut server,
2525 "/api/v8/core/org/someorg/courses",
2526 r#"
2527 [
2528 {
2529 "id": 13,
2530 "name": "organizationid-coursename",
2531 "title": "coursetitle",
2532 "description": "description of the course",
2533 "details_url": "https://tmc.mooc.fi/api/v8/core/courses/13",
2534 "unlock_url": "https://tmc.mooc.fi/api/v8/core/courses/13/unlock",
2535 "reviews_url": "https://tmc.mooc.fi/api/v8/core/courses/13/reviews",
2536 "comet_url": "https://tmc.mooc.fi:8443/comet",
2537 "spyware_urls": [
2538 "http://mooc.spyware.testmycode.net/"
2539 ]
2540 }
2541]
2542"#,
2543 );
2544
2545 let _res = core::get_organization_courses(client, "someorg").unwrap();
2546 }
2547
2548 #[test]
2549 fn core_download_submission() {
2550 init();
2551 let mut server = Server::new();
2552
2553 let client = &make_client(&server);
2554 let _m = server
2555 .mock("GET", "/api/v8/core/submissions/0/download")
2556 .match_query(client_matcher())
2557 .with_body(b"1234")
2558 .create();
2559
2560 let mut temp = tempfile::tempfile().unwrap();
2561 core::download_submission(client, 0, &mut temp).unwrap();
2562 let mut buf = vec![];
2563 temp.rewind().unwrap();
2564 temp.read_to_end(&mut buf).unwrap();
2565 assert!(!buf.is_empty());
2566 }
2567
2568 #[test]
2569 fn core_post_submission_feedback() {
2570 init();
2571 let mut server = Server::new();
2572
2573 let client = &make_client(&server);
2574 let _m = server
2575 .mock("POST", "/api/v8/core/submissions/0/feedback")
2576 .match_query(client_matcher())
2577 .match_body(Matcher::AllOf(vec![
2578 Matcher::UrlEncoded("answers[0][question_id]".to_string(), "0".to_string()),
2579 Matcher::UrlEncoded("answers[0][answer]".to_string(), "ans".to_string()),
2580 ]))
2581 .with_body(
2582 r#"
2583 {
2584 "api_version": 0,
2585 "status": "processing"
2586 }"#,
2587 )
2588 .create();
2589
2590 let _res = core::post_submission_feedback(
2591 client,
2592 0,
2593 vec![FeedbackAnswer {
2594 answer: "ans".to_string(),
2595 question_id: 0,
2596 }],
2597 )
2598 .unwrap();
2599 }
2600
2601 #[test]
2602 fn core_post_submission_review() {
2603 init();
2604 let mut server = Server::new();
2605
2606 let client = &make_client(&server);
2607 let _m = server
2608 .mock("POST", "/api/v8/core/submissions/0/reviews")
2609 .match_query(client_matcher())
2610 .match_body(Matcher::UrlEncoded(
2611 "review[review_body]".to_string(),
2612 "review".to_string(),
2613 ))
2614 .create();
2615
2616 core::post_submission_review(client, 0, "review".to_string()).unwrap();
2617 }
2618}