From b4eb110bd880f78c5da578fe897ae97d4c734984 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 20 Jul 2018 14:33:09 -0700 Subject: rust: base32 encoded idents --- rust/src/api_helpers.rs | 21 ++++++++++++++ rust/src/api_server.rs | 64 +++++++++++++++++++++---------------------- rust/src/api_wrappers.rs | 26 ++++++++++++------ rust/src/database_models.rs | 5 ++-- rust/src/lib.rs | 26 ++++-------------- rust/tests/test_api_server.rs | 64 +++++++++++++++++++++++++------------------ rust/tests/test_fcid.rs | 20 ++++++++++++++ 7 files changed, 137 insertions(+), 89 deletions(-) create mode 100644 rust/tests/test_fcid.rs diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index 62fc4569..1ee08c76 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -1,8 +1,10 @@ +use data_encoding::BASE32_NOPAD; use database_models::*; use database_schema::*; use diesel; use diesel::prelude::*; use errors::*; +use uuid::Uuid; pub fn get_or_create_editgroup(editor_id: i64, conn: &PgConnection) -> Result { // check for current active @@ -85,3 +87,22 @@ pub fn accept_editgroup(editgroup_id: i64, conn: &PgConnection) -> Result Result { + if fcid.len() != 26 { + return Err(ErrorKind::InvalidFatcatId(fcid.to_string()).into()); + } + let mut raw = vec![0; 16]; + BASE32_NOPAD + .decode_mut(fcid.to_uppercase().as_bytes(), &mut raw) + .map_err(|_dp| ErrorKind::InvalidFatcatId(fcid.to_string()))?; + // unwrap() is safe here, because we know raw is always 16 bytes + Ok(Uuid::from_bytes(&raw).unwrap()) +} + +/// Convert UUID to fatcat ID string (base32 encoded) +pub fn uuid2fcid(id: &Uuid) -> String { + let raw = id.as_bytes(); + BASE32_NOPAD.encode(raw).to_lowercase() +} diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index bd211d1f..b7b19a85 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -1,7 +1,7 @@ //! API endpoint handlers use ConnectionPool; -use api_helpers::{get_or_create_editgroup, accept_editgroup}; +use api_helpers::{accept_editgroup, get_or_create_editgroup, fcid2uuid, uuid2fcid}; use chrono; use database_models::*; use database_schema::{changelog, container_edit, container_ident, container_rev, creator_edit, @@ -40,7 +40,7 @@ macro_rules! entity_history_handler { limit: Option, ) -> Result> { let conn = self.db_pool.get().expect("db_pool error"); - let id = uuid::Uuid::parse_str(&id)?; + let id = fcid2uuid(&id)?; let limit = limit.unwrap_or(50); let rows: Vec<(EditgroupRow, ChangelogRow, $edit_row_type)> = editgroup::table @@ -86,8 +86,8 @@ fn container_row2entity( let (state, ident_id, redirect_id) = match ident { Some(i) => ( Some(i.state().unwrap().shortname()), - Some(i.id.to_string()), - i.redirect_id.map(|u| u.to_string()), + Some(uuid2fcid(&i.id)), + i.redirect_id.map(|u| uuid2fcid(&u)), ), None => (None, None, None), }; @@ -110,8 +110,8 @@ fn creator_row2entity(ident: Option, rev: CreatorRevRow) -> Res let (state, ident_id, redirect_id) = match ident { Some(i) => ( Some(i.state().unwrap().shortname()), - Some(i.id.to_string()), - i.redirect_id.map(|u| u.to_string()), + Some(uuid2fcid(&i.id)), + i.redirect_id.map(|u| uuid2fcid(&u)), ), None => (None, None, None), }; @@ -137,8 +137,8 @@ fn file_row2entity( let (state, ident_id, redirect_id) = match ident { Some(i) => ( Some(i.state().unwrap().shortname()), - Some(i.id.to_string()), - i.redirect_id.map(|u| u.to_string()), + Some(uuid2fcid(&i.id)), + i.redirect_id.map(|u| uuid2fcid(&u)), ), None => (None, None, None), }; @@ -147,7 +147,7 @@ fn file_row2entity( .filter(file_release::file_rev.eq(rev.id)) .get_results(conn)? .iter() - .map(|r: &FileReleaseRow| r.target_release_ident_id.to_string()) + .map(|r: &FileReleaseRow| uuid2fcid(&r.target_release_ident_id)) .collect(); Ok(FileEntity { @@ -175,8 +175,8 @@ fn release_row2entity( let (state, ident_id, redirect_id) = match ident { Some(i) => ( Some(i.state().unwrap().shortname()), - Some(i.id.to_string()), - i.redirect_id.map(|u| u.to_string()), + Some(uuid2fcid(&i.id)), + i.redirect_id.map(|u| uuid2fcid(&u)), ), None => (None, None, None), }; @@ -195,7 +195,7 @@ fn release_row2entity( year: r.year.clone(), title: r.title.clone(), locator: r.locator.clone(), - target_release_id: r.target_release_ident_id.map(|v| v.to_string()), + target_release_id: r.target_release_ident_id.map(|v| uuid2fcid(&v)), }) .collect(); @@ -209,7 +209,7 @@ fn release_row2entity( index: c.index, role: c.role.clone(), raw: c.raw.clone(), - creator_id: c.creator_ident_id.map(|v| v.to_string()), + creator_id: c.creator_ident_id.map(|v| uuid2fcid(&v)), }) .collect(); @@ -224,10 +224,10 @@ fn release_row2entity( volume: rev.volume, issue: rev.issue, pages: rev.pages, - container_id: rev.container_ident_id.map(|u| u.to_string()), + container_id: rev.container_ident_id.map(|u| uuid2fcid(&u)), publisher: rev.publisher, language: rev.language, - work_id: Some(rev.work_ident_id.to_string()), + work_id: Some(uuid2fcid(&rev.work_ident_id)), refs: Some(refs), contribs: Some(contribs), state: state, @@ -243,8 +243,8 @@ fn work_row2entity(ident: Option, rev: WorkRevRow) -> Result ( Some(i.state().unwrap().shortname()), - Some(i.id.to_string()), - i.redirect_id.map(|u| u.to_string()), + Some(uuid2fcid(&i.id)), + i.redirect_id.map(|u| uuid2fcid(&u)), ), None => (None, None, None), }; @@ -262,7 +262,7 @@ fn work_row2entity(ident: Option, rev: WorkRevRow) -> Result Result { let conn = self.db_pool.get().expect("db_pool error"); - let id = uuid::Uuid::parse_str(&id)?; + let id = fcid2uuid(&id)?; // TODO: handle Deletions let (ident, rev): (ContainerIdentRow, ContainerRevRow) = container_ident::table @@ -288,7 +288,7 @@ impl Server { pub fn get_creator_handler(&self, id: String) -> Result { let conn = self.db_pool.get().expect("db_pool error"); - let id = uuid::Uuid::parse_str(&id)?; + let id = fcid2uuid(&id)?; let (ident, rev): (CreatorIdentRow, CreatorRevRow) = creator_ident::table .find(id) @@ -313,7 +313,7 @@ impl Server { pub fn get_creator_releases_handler(&self, id: String) -> Result> { let conn = self.db_pool.get().expect("db_pool error"); - let id = uuid::Uuid::parse_str(&id)?; + let id = fcid2uuid(&id)?; // TODO: some kind of unique or group-by? let rows: Vec<(ReleaseRevRow, ReleaseIdentRow, ReleaseContribRow)> = release_rev::table @@ -331,7 +331,7 @@ impl Server { pub fn get_file_handler(&self, id: String) -> Result { let conn = self.db_pool.get().expect("db_pool error"); - let id = uuid::Uuid::parse_str(&id)?; + let id = fcid2uuid(&id)?; let (ident, rev): (FileIdentRow, FileRevRow) = file_ident::table .find(id) @@ -356,7 +356,7 @@ impl Server { pub fn get_release_handler(&self, id: String) -> Result { let conn = self.db_pool.get().expect("db_pool error"); - let id = uuid::Uuid::parse_str(&id)?; + let id = fcid2uuid(&id)?; let (ident, rev): (ReleaseIdentRow, ReleaseRevRow) = release_ident::table .find(id) @@ -381,7 +381,7 @@ impl Server { pub fn get_release_files_handler(&self, id: String) -> Result> { let conn = self.db_pool.get().expect("db_pool error"); - let id = uuid::Uuid::parse_str(&id)?; + let id = fcid2uuid(&id)?; let rows: Vec<(FileRevRow, FileIdentRow, FileReleaseRow)> = file_rev::table .inner_join(file_ident::table) @@ -398,7 +398,7 @@ impl Server { pub fn get_work_handler(&self, id: String) -> Result { let conn = self.db_pool.get().expect("db_pool error"); - let id = uuid::Uuid::parse_str(&id)?; + let id = fcid2uuid(&id)?; let (ident, rev): (WorkIdentRow, WorkRevRow) = work_ident::table .find(id) @@ -410,7 +410,7 @@ impl Server { pub fn get_work_releases_handler(&self, id: String) -> Result> { let conn = self.db_pool.get().expect("db_pool error"); - let id = uuid::Uuid::parse_str(&id)?; + let id = fcid2uuid(&id)?; let rows: Vec<(ReleaseRevRow, ReleaseIdentRow)> = release_rev::table .inner_join(release_ident::table) @@ -559,7 +559,8 @@ impl Server { .iter() .map(|r| FileReleaseRow { file_rev: edit.rev_id.unwrap(), - target_release_ident_id: uuid::Uuid::parse_str(r).expect("valid UUID"), + target_release_ident_id: fcid2uuid(r) + .expect("invalid fatcat identifier"), }) .collect(); let release_rows: Vec = insert_into(file_release::table) @@ -595,7 +596,7 @@ impl Server { }; let work_id = match entity.work_id { - Some(work_id) => uuid::Uuid::parse_str(&work_id)?, + Some(work_id) => fcid2uuid(&work_id)?, None => { // If a work_id wasn't passed, create a new work under the current editgroup let work_model = models::WorkEntity { @@ -608,12 +609,12 @@ impl Server { extra: None, }; let new_entity = self.create_work_handler(work_model, Some(&conn))?; - uuid::Uuid::parse_str(&new_entity.ident)? + fcid2uuid(&new_entity.ident)? } }; let container_id: Option = match entity.container_id { - Some(id) => Some(uuid::Uuid::parse_str(&id)?), + Some(id) => Some(fcid2uuid(&id)?), None => None, }; @@ -657,7 +658,7 @@ impl Server { release_rev: edit.rev_id.unwrap(), target_release_ident_id: r.target_release_id .clone() - .map(|v| uuid::Uuid::parse_str(&v).expect("valid UUID")), + .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), index: r.index, key: r.key.clone(), container_title: r.container_title.clone(), @@ -688,7 +689,7 @@ impl Server { release_rev: edit.rev_id.unwrap(), creator_ident_id: c.creator_id .clone() - .map(|v| uuid::Uuid::parse_str(&v).expect("valid UUID")), + .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), index: c.index, role: c.role.clone(), raw: c.raw.clone(), @@ -1005,4 +1006,3 @@ impl Server { entity_history_handler!(get_release_history_handler, ReleaseEditRow, release_edit); entity_history_handler!(get_work_history_handler, WorkEditRow, work_edit); } - diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index 8651252c..e10906a8 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -1,7 +1,7 @@ //! API endpoint handlers -use errors::*; use api_server::Server; +use errors::*; use fatcat_api::models; use fatcat_api::models::*; use fatcat_api::*; @@ -32,6 +32,8 @@ macro_rules! wrap_entity_handlers { $get_resp::NotFound(ErrorResponse { message: format!("No such entity {}: {}", stringify!($model), id) }), Err(Error(ErrorKind::Uuid(e), _)) => $get_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(Error(ErrorKind::InvalidFatcatId(e), _)) => + $get_resp::BadRequest(ErrorResponse { message: e.to_string() }), Err(e) => { error!("{}", e); $get_resp::GenericError(ErrorResponse { message: e.to_string() }) @@ -52,6 +54,8 @@ macro_rules! wrap_entity_handlers { $post_resp::BadRequest(ErrorResponse { message: e.to_string() }), Err(Error(ErrorKind::Uuid(e), _)) => $post_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(Error(ErrorKind::InvalidFatcatId(e), _)) => + $post_resp::BadRequest(ErrorResponse { message: e.to_string() }), Err(e) => { error!("{}", e); $post_resp::GenericError(ErrorResponse { message: e.to_string() }) @@ -72,6 +76,8 @@ macro_rules! wrap_entity_handlers { $post_batch_resp::BadRequest(ErrorResponse { message: e.to_string() }), Err(Error(ErrorKind::Uuid(e), _)) => $post_batch_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(Error(ErrorKind::InvalidFatcatId(e), _)) => + $post_batch_resp::BadRequest(ErrorResponse { message: e.to_string() }), Err(e) => { error!("{}", e); $post_batch_resp::GenericError(ErrorResponse { message: e.to_string() }) @@ -93,6 +99,8 @@ macro_rules! wrap_entity_handlers { $get_history_resp::NotFound(ErrorResponse { message: format!("No such entity {}: {}", stringify!($model), id) }), Err(Error(ErrorKind::Uuid(e), _)) => $get_history_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(Error(ErrorKind::InvalidFatcatId(e), _)) => + $get_history_resp::BadRequest(ErrorResponse { message: e.to_string() }), Err(e) => { error!("{}", e); $get_history_resp::GenericError(ErrorResponse { message: e.to_string() }) @@ -259,17 +267,18 @@ impl Api for Server { id: i64, _context: &Context, ) -> Box + Send> { - let ret = match self.accept_editgroup_handler(id) { Ok(()) => AcceptEditgroupResponse::MergedSuccessfully(Success { message: "horray!".to_string(), }), - Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => - AcceptEditgroupResponse::NotFound( - ErrorResponse { message: format!("No such editgroup: {}", id) }), - Err(e) => - AcceptEditgroupResponse::GenericError( - ErrorResponse { message: e.to_string() }), + Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => { + AcceptEditgroupResponse::NotFound(ErrorResponse { + message: format!("No such editgroup: {}", id), + }) + } + Err(e) => AcceptEditgroupResponse::GenericError(ErrorResponse { + message: e.to_string(), + }), }; Box::new(futures::done(Ok(ret))) } @@ -298,7 +307,6 @@ impl Api for Server { entity: models::Editgroup, _context: &Context, ) -> Box + Send> { - let ret = match self.create_editgroup_handler(entity) { Ok(eg) => CreateEditgroupResponse::SuccessfullyCreated(eg), diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs index 8489b336..15dffad0 100644 --- a/rust/src/database_models.rs +++ b/rust/src/database_models.rs @@ -1,3 +1,4 @@ +use api_helpers::uuid2fcid; use chrono; use database_schema::*; use errors::*; @@ -54,8 +55,8 @@ macro_rules! entity_structs { Ok(EntityEdit { editgroup_id: self.editgroup_id, revision: self.rev_id, - redirect_ident: self.redirect_id.map(|v| v.to_string()), - ident: self.ident_id.to_string(), + redirect_ident: self.redirect_id.map(|v| uuid2fcid(&v)), + ident: uuid2fcid(&self.ident_id), edit_id: self.id, extra: self.extra_json, }) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 9de94d86..86e367e4 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -33,7 +33,12 @@ mod errors { Uuid(::uuid::ParseError); Io(::std::io::Error) #[cfg(unix)]; Serde(::serde_json::Error); - Base32(::data_encoding::DecodeError); + } + errors { + InvalidFatcatId(id: String) { + description("invalid fatcat identifier syntax") + display("invalid fatcat identifier (expect 26-char base32 encoded): {}", id) + } } } } @@ -49,7 +54,6 @@ use dotenv::dotenv; use iron::middleware::AfterMiddleware; use iron::{Request, Response}; use std::env; -use data_encoding::BASE32_NOPAD; #[cfg(feature = "postgres")] embed_migrations!("../migrations/"); @@ -103,21 +107,3 @@ impl AfterMiddleware for XClacksOverheadMiddleware { Ok(res) } } - -/// Convert fatcat IDs (base32 strings) to UUID -pub fn fcid2uuid(fcid: &str) -> Result { - if fcid.len() != 20 { - bail!("invalid fatcat ID (expecting 20-chars of base32"); - } - let mut raw = vec![0; 16]; - BASE32_NOPAD.decode_mut(fcid.to_uppercase().as_bytes(), &mut raw) - .map_err(|dp| dp.error)?; - // unwrap() is safe here, because we know raw is always 16 bytes - Ok(uuid::Uuid::from_bytes(&raw).unwrap()) -} - -/// Convert UUID to fatcat ID string (base32 encoded) -pub fn uuid2fcid(id: &uuid::Uuid) -> String { - let raw = id.as_bytes(); - BASE32_NOPAD.encode(raw).to_lowercase() -} diff --git a/rust/tests/test_api_server.rs b/rust/tests/test_api_server.rs index b00d22eb..e7ac9585 100644 --- a/rust/tests/test_api_server.rs +++ b/rust/tests/test_api_server.rs @@ -47,7 +47,7 @@ fn test_entity_gets() { check_response( request::get( - "http://localhost:9411/v0/container/00000000-0000-0000-1111-000000000002", + "http://localhost:9411/v0/container/aaaaaaaaaaaaaeiraaaaaaaaai", headers.clone(), &router, ), @@ -57,7 +57,7 @@ fn test_entity_gets() { check_response( request::get( - "http://localhost:9411/v0/creator/00000000-0000-0000-2222-000000000001", + "http://localhost:9411/v0/creator/aaaaaaaaaaaaaircaaaaaaaaae", headers.clone(), &router, ), @@ -67,7 +67,7 @@ fn test_entity_gets() { check_response( request::get( - "http://localhost:9411/v0/file/00000000-0000-0000-3333-000000000002", + "http://localhost:9411/v0/file/aaaaaaaaaaaaamztaaaaaaaaai", headers.clone(), &router, ), @@ -77,7 +77,7 @@ fn test_entity_gets() { check_response( request::get( - "http://localhost:9411/v0/release/00000000-0000-0000-4444-000000000002", + "http://localhost:9411/v0/release/aaaaaaaaaaaaarceaaaaaaaaai", headers.clone(), &router, ), @@ -87,7 +87,7 @@ fn test_entity_gets() { check_response( request::get( - "http://localhost:9411/v0/work/00000000-0000-0000-5555-000000000002", + "http://localhost:9411/v0/work/aaaaaaaaaaaaavkvaaaaaaaaai", headers.clone(), &router, ), @@ -102,7 +102,7 @@ fn test_entity_404() { check_response( request::get( - "http://localhost:9411/v0/creator/00000000-0000-0000-2222-999999999999", + "http://localhost:9411/v0/creator/aaaaaaaaaaaaairceeeeeeeeee", headers.clone(), &router, ), @@ -117,7 +117,7 @@ fn test_entity_history() { check_response( request::get( - "http://localhost:9411/v0/container/00000000-0000-0000-1111-000000000002/history", + "http://localhost:9411/v0/container/aaaaaaaaaaaaaeiraaaaaaaaai/history", headers.clone(), &router, ), @@ -127,7 +127,7 @@ fn test_entity_history() { check_response( request::get( - "http://localhost:9411/v0/creator/00000000-0000-0000-2222-000000000001/history", + "http://localhost:9411/v0/creator/aaaaaaaaaaaaaircaaaaaaaaae/history", headers.clone(), &router, ), @@ -137,7 +137,7 @@ fn test_entity_history() { check_response( request::get( - "http://localhost:9411/v0/file/00000000-0000-0000-3333-000000000002/history", + "http://localhost:9411/v0/file/aaaaaaaaaaaaamztaaaaaaaaai/history", headers.clone(), &router, ), @@ -147,7 +147,7 @@ fn test_entity_history() { check_response( request::get( - "http://localhost:9411/v0/release/00000000-0000-0000-4444-000000000002/history", + "http://localhost:9411/v0/release/aaaaaaaaaaaaarceaaaaaaaaai/history", headers.clone(), &router, ), @@ -157,7 +157,7 @@ fn test_entity_history() { check_response( request::get( - "http://localhost:9411/v0/work/00000000-0000-0000-5555-000000000002/history", + "http://localhost:9411/v0/work/aaaaaaaaaaaaavkvaaaaaaaaai/history", headers.clone(), &router, ), @@ -207,7 +207,7 @@ fn test_reverse_lookups() { check_response( request::get( - "http://localhost:9411/v0/creator/00000000-0000-0000-2222-000000000002/releases", + "http://localhost:9411/v0/creator/aaaaaaaaaaaaaircaaaaaaaaai/releases", headers.clone(), &router, ), @@ -217,7 +217,7 @@ fn test_reverse_lookups() { check_response( request::get( - "http://localhost:9411/v0/release/00000000-0000-0000-4444-000000000002/files", + "http://localhost:9411/v0/release/aaaaaaaaaaaaarceaaaaaaaaai/files", headers.clone(), &router, ), @@ -227,7 +227,7 @@ fn test_reverse_lookups() { check_response( request::get( - "http://localhost:9411/v0/work/00000000-0000-0000-5555-000000000002/releases", + "http://localhost:9411/v0/work/aaaaaaaaaaaaavkvaaaaaaaaai/releases", headers.clone(), &router, ), @@ -310,8 +310,8 @@ fn test_post_file() { "url": "http://archive.org/asdf.txt", "mimetype": "application/pdf", "releases": [ - "00000000-0000-0000-4444-000000000001", - "00000000-0000-0000-4444-000000000002" + "aaaaaaaaaaaaarceaaaaaaaaae", + "aaaaaaaaaaaaarceaaaaaaaaai" ], "extra": { "source": "speculation" } }"#, @@ -333,7 +333,7 @@ fn test_post_release() { // TODO: target_release_id r#"{"title": "secret minimal paper", "release_type": "journal-article", - "work_id": "00000000-0000-0000-5555-000000000001" + "work_id": "aaaaaaaaaaaaavkvaaaaaaaaae" }"#, &router, ), @@ -367,8 +367,8 @@ fn test_post_release() { "volume": "439", "issue": "IV", "pages": "1-399", - "work_id": "00000000-0000-0000-5555-000000000002", - "container_id": "00000000-0000-0000-1111-000000000001", + "work_id": "aaaaaaaaaaaaavkvaaaaaaaaai", + "container_id": "aaaaaaaaaaaaaeiraaaaaaaaae", "refs": [{ "index": 3, "raw": "just a string" @@ -378,7 +378,7 @@ fn test_post_release() { "contribs": [{ "index": 1, "raw": "textual description of contributor (aka, name)", - "creator_id": "00000000-0000-0000-2222-000000000001", + "creator_id": "aaaaaaaaaaaaaircaaaaaaaaae", "contrib_type": "author" },{ "raw": "shorter" @@ -505,13 +505,21 @@ fn test_changelog() { let (headers, router, _conn) = setup(); check_response( - request::get("http://localhost:9411/v0/changelog", headers.clone(), &router), + request::get( + "http://localhost:9411/v0/changelog", + headers.clone(), + &router, + ), status::Ok, Some("editgroup_id"), ); check_response( - request::get("http://localhost:9411/v0/changelog/1", headers.clone(), &router), + request::get( + "http://localhost:9411/v0/changelog/1", + headers.clone(), + &router, + ), status::Ok, Some("files"), ); @@ -527,7 +535,11 @@ fn test_stats() { Some("merged_editgroups"), ); check_response( - request::get("http://localhost:9411/v0/stats?more=yes", headers.clone(), &router), + request::get( + "http://localhost:9411/v0/stats?more=yes", + headers.clone(), + &router, + ), status::Ok, Some("merged_editgroups"), ); @@ -547,8 +559,8 @@ fn test_400() { "volume": "439", "issue": "IV", "pages": "1-399", - "work_id": "00000000-0000-0000-5555-00002", - "container_id": "00000000-0000-0000-1111-000000001", + "work_id": "aaaaaaaaaaaaavkvaaaaaaaaae", + "container_id": "aaaaaaaaaaaaaeiraaaaaae", "refs": [{ "index": 3, "raw": "just a string" @@ -558,7 +570,7 @@ fn test_400() { "contribs": [{ "index": 1, "raw": "textual description of contributor (aka, name)", - "creator_id": "00000000-0000-0000-2222-000000000001", + "creator_id": "aaaaaaaaaaaaaircaaaaaaaaae", "contrib_type": "author" },{ "raw": "shorter" diff --git a/rust/tests/test_fcid.rs b/rust/tests/test_fcid.rs new file mode 100644 index 00000000..c9f6c00d --- /dev/null +++ b/rust/tests/test_fcid.rs @@ -0,0 +1,20 @@ +extern crate fatcat; +extern crate uuid; + +use fatcat::api_helpers::{fcid2uuid, uuid2fcid}; +use uuid::Uuid; + +#[test] +fn test_fcid_conversions() { + let test_uuid = Uuid::parse_str("86daea5b-1b6b-432a-bb67-ea97795f80fe").unwrap(); + let test_fcid = "q3nouwy3nnbsvo3h5klxsx4a7y"; + + assert_eq!(test_fcid, uuid2fcid(&test_uuid)); + assert_eq!(test_uuid, fcid2uuid(test_fcid).unwrap()); + assert_eq!(test_uuid, fcid2uuid(&test_fcid.to_uppercase()).unwrap()); + assert_eq!(test_uuid, fcid2uuid(&uuid2fcid(&test_uuid)).unwrap()); + + assert_eq!(false, fcid2uuid("asdf").is_ok()); + assert_eq!(false, fcid2uuid("q3nouwy3nnbsvo3h5klx").is_ok()); + assert_eq!(false, fcid2uuid("10Oouwy3nnbsvo3h5klxsx4a7y").is_ok()); +} -- cgit v1.2.3