From c4618dd2fb070eefcdb38bca7b92fe32da766702 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Wed, 25 Jul 2018 12:18:34 -0700 Subject: more external ident handling --- rust/Cargo.lock | 24 +++++ rust/Cargo.toml | 2 + rust/src/api_helpers.rs | 84 +++++++++++++++++ rust/src/api_server.rs | 55 +++++++++-- rust/src/api_wrappers.rs | 8 ++ rust/src/lib.rs | 7 ++ rust/tests/test_api_server.rs | 215 +++++++++++++++++++++++++++++++++++++++++- 7 files changed, 380 insertions(+), 15 deletions(-) (limited to 'rust') diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 07bf17bf..0603c7f2 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -316,7 +316,9 @@ dependencies = [ "iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "iron-slog 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "iron-test 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "slog-async 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -832,6 +834,18 @@ dependencies = [ "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex-syntax" version = "0.5.6" @@ -840,6 +854,14 @@ dependencies = [ "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex-syntax" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "remove_dir_all" version = "0.5.1" @@ -1350,7 +1372,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" +"checksum regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5bbbea44c5490a1e84357ff28b7d518b4619a159fed5d25f6c1de2d19cc42814" "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" +"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum route-recognizer 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3255338088df8146ba63d60a9b8e3556f1146ce2973bc05a75181a42ce2256" "checksum router 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dc63b6f3b8895b0d04e816b2b1aa58fdba2d5acca3cbb8f0ab8e017347d57397" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0868d7f1..23747ddf 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -16,6 +16,8 @@ error-chain = "0.12" uuid = "0.5" log = "*" data-encoding = "2.1" +regex = "1" +lazy_static = "1.0" # API server chrono = { version = "0.4", features = ["serde"] } diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index f0f56a6b..ef07ee55 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -5,6 +5,8 @@ use diesel; use diesel::prelude::*; use errors::*; use uuid::Uuid; +use regex::Regex; + pub fn get_or_create_editgroup(editor_id: Uuid, conn: &PgConnection) -> Result { // check for current active @@ -109,3 +111,85 @@ pub fn uuid2fcid(id: &Uuid) -> String { let raw = id.as_bytes(); BASE32_NOPAD.encode(raw).to_lowercase() } + +pub fn check_pmcid(raw: &str) -> Result<()> { + lazy_static! { + static ref RE: Regex = Regex::new(r"^PMC\d+$").unwrap(); + } + if RE.is_match(raw) { + Ok(()) + } else { + Err(ErrorKind::MalformedExternalId( + format!("not a valid PubMed Central ID (PMCID): '{}' (expected, eg, 'PMC12345')", raw) + ).into()) + } +} + +pub fn check_pmid(raw: &str) -> Result<()> { + lazy_static! { + static ref RE: Regex = Regex::new(r"^\d+$").unwrap(); + } + if RE.is_match(raw) { + Ok(()) + } else { + Err(ErrorKind::MalformedExternalId( + format!("not a valid PubMed ID (PMID): '{}' (expected, eg, '1234')", raw) + ).into()) + } +} + +pub fn check_wikidata_qid(raw: &str) -> Result<()> { + lazy_static! { + static ref RE: Regex = Regex::new(r"^Q\d+$").unwrap(); + } + if RE.is_match(raw) { + Ok(()) + } else { + Err(ErrorKind::MalformedExternalId( + format!("not a valid Wikidata QID: '{}' (expected, eg, 'Q1234')", raw) + ).into()) + } +} + +pub fn check_doi(raw: &str) -> Result<()> { + lazy_static! { + static ref RE: Regex = Regex::new(r"^10.\d{3,6}/.+$").unwrap(); + } + if RE.is_match(raw) { + Ok(()) + } else { + Err(ErrorKind::MalformedExternalId( + format!("not a valid DOI: '{}' (expected, eg, '10.1234/aksjdfh')", raw) + ).into()) + } +} + +pub fn check_issn(raw: &str) -> Result<()> { + lazy_static! { + static ref RE: Regex = Regex::new(r"^\d{4}-\d{3}[0-9X]$").unwrap(); + } + if RE.is_match(raw) { + Ok(()) + } else { + Err(ErrorKind::MalformedExternalId( + format!("not a valid ISSN: '{}' (expected, eg, '1234-5678')", raw) + ).into()) + } +} + +pub fn check_orcid(raw: &str) -> Result<()> { + lazy_static! { + static ref RE: Regex = Regex::new(r"^\d{4}-\d{4}-\d{4}-\d{4}$").unwrap(); + } + if RE.is_match(raw) { + Ok(()) + } else { + Err(ErrorKind::MalformedExternalId( + format!("not a valid ORCID: '{}' (expected, eg, '0123-4567-3456-6789')", raw) + ).into()) + } +} + +// TODO: make the above checks "more correct" +// TODO: check ISBN-13 +// TODO: check hashes (SHA-1, etc) diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 5aa075dd..64c028be 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -1,6 +1,6 @@ //! API endpoint handlers -use api_helpers::{accept_editgroup, fcid2uuid, get_or_create_editgroup, uuid2fcid}; +use api_helpers::*; use chrono; use database_models::*; use database_schema::{ @@ -95,6 +95,7 @@ fn container_row2entity( }; Ok(ContainerEntity { issnl: rev.issnl, + wikidata_qid: rev.wikidata_qid, publisher: rev.publisher, name: rev.name, abbrev: rev.abbrev, @@ -122,6 +123,7 @@ fn creator_row2entity(ident: Option, rev: CreatorRevRow) -> Res given_name: rev.given_name, surname: rev.surname, orcid: rev.orcid, + wikidata_qid: rev.wikidata_qid, state: state, ident: ident_id, revision: Some(rev.id.to_string()), @@ -249,6 +251,7 @@ fn release_row2entity( pmid: rev.pmid, pmcid: rev.pmcid, isbn13: rev.isbn13, + wikidata_qid: rev.wikidata_qid, volume: rev.volume, issue: rev.issue, pages: rev.pages, @@ -304,6 +307,7 @@ impl Server { pub fn lookup_container_handler(&self, issnl: &str) -> Result { let conn = self.db_pool.get().expect("db_pool error"); + check_issn(issnl)?; let (ident, rev): (ContainerIdentRow, ContainerRevRow) = container_ident::table .inner_join(container_rev::table) .filter(container_rev::issnl.eq(issnl)) @@ -329,6 +333,7 @@ impl Server { pub fn lookup_creator_handler(&self, orcid: &str) -> Result { let conn = self.db_pool.get().expect("db_pool error"); + check_orcid(orcid)?; let (ident, rev): (CreatorIdentRow, CreatorRevRow) = creator_ident::table .inner_join(creator_rev::table) .filter(creator_rev::orcid.eq(orcid)) @@ -397,6 +402,7 @@ impl Server { pub fn lookup_release_handler(&self, doi: &str) -> Result { let conn = self.db_pool.get().expect("db_pool error"); + check_doi(doi)?; let (ident, rev): (ReleaseIdentRow, ReleaseRevRow) = release_ident::table .inner_join(release_rev::table) .filter(release_rev::doi.eq(doi)) @@ -472,20 +478,27 @@ impl Server { None => get_or_create_editgroup(editor_id, &conn)?, Some(param) => fcid2uuid(¶m)?, }; + if let Some(ref extid) = entity.wikidata_qid { + check_wikidata_qid(extid)?; + } + if let Some(ref extid) = entity.issnl { + check_issn(extid)?; + } let edit: ContainerEditRow = diesel::sql_query( - "WITH rev AS ( INSERT INTO container_rev (name, publisher, issnl, abbrev, coden, extra_json) - VALUES ($1, $2, $3, $4, $5, $6) + "WITH rev AS ( INSERT INTO container_rev (name, publisher, issnl, wikidata_qid, abbrev, coden, extra_json) + VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id ), ident AS ( INSERT INTO container_ident (rev_id) VALUES ((SELECT rev.id FROM rev)) RETURNING id ) INSERT INTO container_edit (editgroup_id, ident_id, rev_id) VALUES - ($7, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) + ($8, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) RETURNING *", ).bind::(entity.name) .bind::, _>(entity.publisher) .bind::, _>(entity.issnl) + .bind::, _>(entity.wikidata_qid) .bind::, _>(entity.abbrev) .bind::, _>(entity.coden) .bind::, _>(entity.extra) @@ -514,21 +527,28 @@ impl Server { None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), Some(param) => fcid2uuid(¶m)?, }; + if let Some(ref extid) = entity.orcid { + check_orcid(extid)?; + } + if let Some(ref extid) = entity.wikidata_qid { + check_wikidata_qid(extid)?; + } let edit: CreatorEditRow = diesel::sql_query( - "WITH rev AS ( INSERT INTO creator_rev (display_name, given_name, surname, orcid, extra_json) - VALUES ($1, $2, $3, $4, $5) + "WITH rev AS ( INSERT INTO creator_rev (display_name, given_name, surname, orcid, wikidata_qid, extra_json) + VALUES ($1, $2, $3, $4, $5, $6) RETURNING id ), ident AS ( INSERT INTO creator_ident (rev_id) VALUES ((SELECT rev.id FROM rev)) RETURNING id ) INSERT INTO creator_edit (editgroup_id, ident_id, rev_id) VALUES - ($6, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) + ($7, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) RETURNING *", ).bind::(entity.display_name) .bind::, _>(entity.given_name) .bind::, _>(entity.surname) .bind::, _>(entity.orcid) + .bind::, _>(entity.wikidata_qid) .bind::, _>(entity.extra) .bind::(editgroup_id) .get_result(conn)?; @@ -644,6 +664,18 @@ impl Server { None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), Some(param) => fcid2uuid(¶m)?, }; + if let Some(ref extid) = entity.doi { + check_doi(extid)?; + } + if let Some(ref extid) = entity.pmid { + check_pmid(extid)?; + } + if let Some(ref extid) = entity.pmcid { + check_pmcid(extid)?; + } + if let Some(ref extid) = entity.wikidata_qid { + check_wikidata_qid(extid)?; + } let work_id = match entity.work_id { Some(work_id) => fcid2uuid(&work_id)?, @@ -668,14 +700,14 @@ impl Server { }; let edit: ReleaseEditRow = diesel::sql_query( - "WITH rev AS ( INSERT INTO release_rev (title, release_type, release_status, release_date, doi, isbn13, volume, issue, pages, work_ident_id, container_ident_id, publisher, language, extra_json) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) + "WITH rev AS ( INSERT INTO release_rev (title, release_type, release_status, release_date, doi, pmid, pmcid, wikidata_qid, isbn13, volume, issue, pages, work_ident_id, container_ident_id, publisher, language, extra_json) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) RETURNING id ), ident AS ( INSERT INTO release_ident (rev_id) VALUES ((SELECT rev.id FROM rev)) RETURNING id ) INSERT INTO release_edit (editgroup_id, ident_id, rev_id) VALUES - ($15, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) + ($18, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) RETURNING *", ).bind::(entity.title) .bind::, _>(entity.release_type) @@ -683,6 +715,9 @@ impl Server { .bind::, _>( entity.release_date.map(|v| v.naive_utc().date())) .bind::, _>(entity.doi) + .bind::, _>(entity.pmid) + .bind::, _>(entity.pmcid) + .bind::, _>(entity.wikidata_qid) .bind::, _>(entity.isbn13) .bind::, _>(entity.volume) .bind::, _>(entity.issue) diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index 8be661e1..e66f3ccd 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -35,6 +35,8 @@ macro_rules! wrap_entity_handlers { Err(Error(ErrorKind::InvalidFatcatId(e), _)) => $get_resp::BadRequest(ErrorResponse { message: ErrorKind::InvalidFatcatId(e).to_string() }), + Err(Error(ErrorKind::MalformedExternalId(e), _)) => + $get_resp::BadRequest(ErrorResponse { message: e.to_string() }), Err(e) => { error!("{}", e); $get_resp::GenericError(ErrorResponse { message: e.to_string() }) @@ -58,6 +60,8 @@ macro_rules! wrap_entity_handlers { Err(Error(ErrorKind::InvalidFatcatId(e), _)) => $post_resp::BadRequest(ErrorResponse { message: ErrorKind::InvalidFatcatId(e).to_string() }), + Err(Error(ErrorKind::MalformedExternalId(e), _)) => + $post_resp::BadRequest(ErrorResponse { message: e.to_string() }), Err(e) => { error!("{}", e); $post_resp::GenericError(ErrorResponse { message: e.to_string() }) @@ -81,6 +85,8 @@ macro_rules! wrap_entity_handlers { Err(Error(ErrorKind::InvalidFatcatId(e), _)) => $post_batch_resp::BadRequest(ErrorResponse { message: ErrorKind::InvalidFatcatId(e).to_string() }), + Err(Error(ErrorKind::MalformedExternalId(e), _)) => + $post_batch_resp::BadRequest(ErrorResponse { message: e.to_string() }), Err(e) => { error!("{}", e); $post_batch_resp::GenericError(ErrorResponse { message: e.to_string() }) @@ -127,6 +133,8 @@ macro_rules! wrap_lookup_handler { $get_resp::FoundEntity(entity), Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => $get_resp::NotFound(ErrorResponse { message: format!("Not found: {}", $idname) }), + Err(Error(ErrorKind::MalformedExternalId(e), _)) => + $get_resp::BadRequest(ErrorResponse { message: e.to_string() }), Err(e) => { error!("{}", e); $get_resp::BadRequest(ErrorResponse { message: e.to_string() }) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index fd871f55..eff487b3 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -17,6 +17,9 @@ extern crate serde_json; #[macro_use] extern crate log; extern crate data_encoding; +extern crate regex; +#[macro_use] +extern crate lazy_static; pub mod api_helpers; pub mod api_server; @@ -39,6 +42,10 @@ mod errors { description("invalid fatcat identifier syntax") display("invalid fatcat identifier (expect 26-char base32 encoded): {}", id) } + MalformedExternalId(id: String) { + description("external identifier doesn't match required pattern") + display("external identifier doesn't match required pattern") + } } } } diff --git a/rust/tests/test_api_server.rs b/rust/tests/test_api_server.rs index b831f122..b9880ba1 100644 --- a/rust/tests/test_api_server.rs +++ b/rust/tests/test_api_server.rs @@ -299,7 +299,7 @@ fn test_post_creator() { #[test] fn test_post_file() { - let (headers, router, _conn) = setup(); + let (headers, router, conn) = setup(); check_response( request::post( @@ -315,12 +315,15 @@ fn test_post_file() { check_response( request::post( "http://localhost:9411/v0/file", - headers, + headers.clone(), r#"{"size": 76543, - "sha1": "f013d66c7f6817d08b7eb2a93e6d0440c1f3e7f8", + "sha1": "f0000000000000008b7eb2a93e6d0440c1f3e7f8", "md5": "0b6d347b01d437a092be84c2edfce72c", "sha256": "a77e4c11a57f1d757fca5754a8f83b5d4ece49a2d28596889127c1a2f3f28832", - "url": "http://archive.org/asdf.txt", + "urls": [ + {"url": "http://archive.org/asdf.txt", "rel": "web" }, + {"url": "http://web.archive.org/2/http://archive.org/asdf.txt", "rel": "webarchive" } + ], "mimetype": "application/pdf", "releases": [ "aaaaaaaaaaaaarceaaaaaaaaae", @@ -332,7 +335,33 @@ fn test_post_file() { ), status::Created, None, - ); // TODO: "secret paper" + ); + + let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001").unwrap(); + let editgroup_id = get_or_create_editgroup(editor_id, &conn).unwrap(); + check_response( + request::post( + &format!( + "http://localhost:9411/v0/editgroup/{}/accept", + uuid2fcid(&editgroup_id) + ), + headers.clone(), + "", + &router, + ), + status::Ok, + None, + ); + + check_response( + request::get( + "http://localhost:9411/v0/file/lookup?sha1=f0000000000000008b7eb2a93e6d0440c1f3e7f8", + headers.clone(), + &router, + ), + status::Ok, + Some("web.archive.org/2/http"), + ); } #[test] @@ -377,6 +406,9 @@ fn test_post_release() { r#"{"title": "secret paper", "release_type": "journal-article", "doi": "10.1234/abcde.781231231239", + "pmid": "54321", + "pmcid": "PMC12345", + "wikidata_qid": "Q12345", "volume": "439", "issue": "IV", "pages": "1-399", @@ -637,3 +669,176 @@ fn test_edit_gets() { None, ); } + +#[test] +fn test_bad_external_idents() { + let (headers, router, _conn) = setup(); + + // Bad wikidata QID + check_response( + request::post( + "http://localhost:9411/v0/release", + headers.clone(), + r#"{"title": "secret paper", + "wikidata_qid": "P12345" + }"#, + &router, + ), + status::BadRequest, + Some("Wikidata QID"), + ); + check_response( + request::post( + "http://localhost:9411/v0/container", + headers.clone(), + r#"{"name": "my journal", + "wikidata_qid": "P12345" + }"#, + &router, + ), + status::BadRequest, + Some("Wikidata QID"), + ); + check_response( + request::post( + "http://localhost:9411/v0/creator", + headers.clone(), + r#"{"display_name": "some body", + "wikidata_qid": "P12345" + }"#, + &router, + ), + status::BadRequest, + Some("Wikidata QID"), + ); + + // Bad PMCID + check_response( + request::post( + "http://localhost:9411/v0/release", + headers.clone(), + r#"{"title": "secret paper", + "pmcid": "12345" + }"#, + &router, + ), + status::BadRequest, + Some("PMCID"), + ); + + // Bad PMID + check_response( + request::post( + "http://localhost:9411/v0/release", + headers.clone(), + r#"{"title": "secret paper", + "pmid": "not-a-number" + }"#, + &router, + ), + status::BadRequest, + Some("PMID"), + ); + + // Bad DOI + check_response( + request::post( + "http://localhost:9411/v0/release", + headers.clone(), + r#"{"title": "secret paper", + "doi": "asdf" + }"#, + &router, + ), + status::BadRequest, + Some("DOI"), + ); + + // Good identifiers + check_response( + request::post( + "http://localhost:9411/v0/release", + headers.clone(), + r#"{"title": "secret paper", + "doi": "10.1234/abcde.781231231239", + "pmid": "54321", + "pmcid": "PMC12345", + "wikidata_qid": "Q12345" + }"#, + &router, + ), + status::Created, + None, + ); +} + +#[test] +fn test_abstracts() { + let (headers, router, conn) = setup(); + + check_response( + request::post( + "http://localhost:9411/v0/release", + headers.clone(), + r#"{"title": "some paper", + "doi": "10.1234/iiiiiii", + "abstracts": [ + {"lang": "zh", + "mimetype": "text/plain", + "content": "some rando abstract 24iu3i25u2" }, + {"lang": "en", + "mimetype": "application/xml+jats", + "content": "some other abstract 99139405" } + ] + }"#, + &router, + ), + status::Created, + None, + ); + + let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001").unwrap(); + let editgroup_id = get_or_create_editgroup(editor_id, &conn).unwrap(); + check_response( + request::post( + &format!( + "http://localhost:9411/v0/editgroup/{}/accept", + uuid2fcid(&editgroup_id) + ), + headers.clone(), + "", + &router, + ), + status::Ok, + None, + ); + + check_response( + request::get( + "http://localhost:9411/v0/release/lookup?doi=10.1234/iiiiiii", + headers.clone(), + &router, + ), + status::Ok, + // SHA-1 of first abstract string + Some("4e30ded694c6a7775b9e7b019dfda6be0dd60944"), + ); + check_response( + request::get( + "http://localhost:9411/v0/release/lookup?doi=10.1234/iiiiiii", + headers.clone(), + &router, + ), + status::Ok, + Some("99139405"), + ); + check_response( + request::get( + "http://localhost:9411/v0/release/lookup?doi=10.1234/iiiiiii", + headers.clone(), + &router, + ), + status::Ok, + Some("24iu3i25u2"), + ); +} -- cgit v1.2.3