Create metadata (#133)
* Added migration for metadata table used to store a song's metadata * Added comment for new table * Added endpoint to create metadata * Filling out code * Changed type of duration in request * Reorganizing code * Changing metadata table to song * Added TODO for data type discrepancy for the future * Fix migration syntax error * Added types to request * Added environment variable for root directory: * Moving keys to its own module * Moved environement related code to its own module * Added function to get root_directory * Added endpoint to create song * Minor fixes * Added test and refactored tests * Ironing out some kinks * Resolved test issue Turns out I was using the wrong id value * Added env variable in workflow * Added TODO * Updated env variable * clippy warning fixes * Making sure date_created is populated when returneD * Code cleanup * Added TODOs * Uncommenting tests * Code formatting
This commit was merged in pull request #133.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
SECRET_MAIN_KEY=refero34o8rfhfjn983thf39fhc943rf923n3h
|
||||
ROOT_DIRECTORY=/home/icarus/mydata
|
||||
POSTGRES_MAIN_USER=icarus
|
||||
POSTGRES_MAIN_PASSWORD=password
|
||||
POSTGRES_MAIN_DB=icarus_db
|
||||
|
||||
@@ -66,6 +66,7 @@ jobs:
|
||||
|
||||
- name: Run tests
|
||||
env:
|
||||
ROOT_DIRECTORY: "/tmp"
|
||||
DATABASE_URL: "postgres://${{ secrets.POSTGRES_USER }}:${{ secrets.POSTGRES_PASSWORD }}@localhost:5432/${{ secrets.POSTGRES_DB }}"
|
||||
run: cargo test --verbose
|
||||
|
||||
|
||||
@@ -26,3 +26,26 @@ CREATE TABLE IF NOT EXISTS "coverartQueue" (
|
||||
|
||||
-- Create an index for better query performance
|
||||
CREATE INDEX metadata_queue_data_metadata ON "metadataQueue" USING gin (metadata);
|
||||
|
||||
-- Table to store a song's info
|
||||
CREATE TABLE IF NOT EXISTS "song" (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
title TEXT NOT NULL,
|
||||
artist TEXT NOT NULL,
|
||||
album_artist TEXT NOT NULL,
|
||||
album TEXT NOT NULL,
|
||||
genre TEXT NOT NULL,
|
||||
-- TODO: Address discrepancy of date and year at some point
|
||||
-- date TEXT NOT NULL,
|
||||
year INT NOT NULL,
|
||||
track SMALLINT NOT NULL,
|
||||
disc SMALLINT NOT NULL,
|
||||
track_count SMALLINT NOT NULL,
|
||||
disc_count SMALLINT NOT NULL,
|
||||
duration INT NOT NULL,
|
||||
audio_type TEXT NOT NULL,
|
||||
date_created timestamptz DEFAULT now(),
|
||||
filename TEXT NOT NULL,
|
||||
directory TEXT NOT NULL,
|
||||
user_id UUID NULL
|
||||
);
|
||||
|
||||
@@ -11,4 +11,6 @@ pub mod endpoints {
|
||||
pub const QUEUECOVERART: &str = "/api/v2/coverart/queue";
|
||||
pub const QUEUECOVERARTDATA: &str = "/api/v2/coverart/queue/data";
|
||||
pub const QUEUECOVERARTLINK: &str = "/api/v2/coverart/queue/link";
|
||||
|
||||
pub const CREATESONG: &str = "/api/v2/song";
|
||||
}
|
||||
|
||||
@@ -13,6 +13,69 @@ pub mod request {
|
||||
pub status: String,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod create_metadata {
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Request {
|
||||
pub title: String,
|
||||
pub artist: String,
|
||||
pub album_artist: String,
|
||||
pub album: String,
|
||||
pub genre: String,
|
||||
pub date: String,
|
||||
pub track: i32,
|
||||
pub disc: i32,
|
||||
pub track_count: i32,
|
||||
pub disc_count: i32,
|
||||
pub duration: i32,
|
||||
pub audio_type: String,
|
||||
pub user_id: uuid::Uuid,
|
||||
pub song_queue_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
!self.title.is_empty()
|
||||
|| !self.artist.is_empty()
|
||||
|| !self.album_artist.is_empty()
|
||||
|| !self.album.is_empty()
|
||||
|| !self.genre.is_empty()
|
||||
|| !self.date.is_empty()
|
||||
|| self.track > 0
|
||||
|| self.disc > 0
|
||||
|| self.track_count > 0
|
||||
|| self.disc_count > 0
|
||||
|| self.duration > 0
|
||||
|| !self.audio_type.is_empty()
|
||||
|| !self.user_id.is_nil()
|
||||
|| !self.song_queue_id.is_nil()
|
||||
}
|
||||
|
||||
pub fn to_song(&self) -> icarus_models::song::Song {
|
||||
icarus_models::song::Song {
|
||||
id: uuid::Uuid::nil(),
|
||||
title: self.title.clone(),
|
||||
artist: self.artist.clone(),
|
||||
album_artist: self.album_artist.clone(),
|
||||
album: self.album.clone(),
|
||||
genre: self.genre.clone(),
|
||||
year: self.date[..3].parse().unwrap(),
|
||||
track: self.track,
|
||||
disc: self.disc,
|
||||
track_count: self.track_count,
|
||||
disc_count: self.disc_count,
|
||||
duration: self.duration,
|
||||
audio_type: self.audio_type.clone(),
|
||||
user_id: self.user_id,
|
||||
// TODO: Change the type of this in icarus_models lib
|
||||
date_created: String::new(),
|
||||
filename: String::new(),
|
||||
data: Vec::new(),
|
||||
directory: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
@@ -55,8 +118,18 @@ pub mod response {
|
||||
pub data: Vec<Vec<u8>>,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod create_metadata {
|
||||
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Response {
|
||||
pub message: String,
|
||||
pub data: Vec<icarus_models::song::Song>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Might make a distinction between year and date in a song's tag at some point
|
||||
|
||||
pub mod status {
|
||||
pub const PENDING: &str = "pending";
|
||||
pub const PROCESSING: &str = "processing";
|
||||
@@ -67,6 +140,60 @@ pub mod status {
|
||||
}
|
||||
}
|
||||
|
||||
mod song_db {
|
||||
use sqlx::Row;
|
||||
|
||||
// TODO: Change first parameter of return value from string to a time type
|
||||
pub async fn insert(
|
||||
pool: &sqlx::PgPool,
|
||||
song: &icarus_models::song::Song,
|
||||
) -> Result<(String, uuid::Uuid), sqlx::Error> {
|
||||
let result = sqlx::query(
|
||||
r#"
|
||||
INSERT INTO "song" (title, artist, album_artist, album, genre, year, track, disc, track_count, disc_count, duration, audio_type, filename, directory, user_id)
|
||||
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) RETURNING date_created, id;
|
||||
"#
|
||||
)
|
||||
.bind(&song.title)
|
||||
.bind(&song.artist)
|
||||
.bind(&song.album_artist)
|
||||
.bind(&song.album)
|
||||
.bind(&song.genre)
|
||||
.bind(song.year)
|
||||
.bind(song.track)
|
||||
.bind(song.disc)
|
||||
.bind(song.track_count)
|
||||
.bind(song.disc_count)
|
||||
.bind(song.duration)
|
||||
.bind(&song.audio_type)
|
||||
.bind(&song.filename)
|
||||
.bind(&song.directory)
|
||||
.bind(song.user_id)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!("Error inserting query: {:?}", e);
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(row) => {
|
||||
let id: uuid::Uuid = row
|
||||
.try_get("id")
|
||||
.map_err(|_e| sqlx::Error::RowNotFound)
|
||||
.unwrap();
|
||||
let date_created_time: time::OffsetDateTime = row
|
||||
.try_get("date_created")
|
||||
.map_err(|_e| sqlx::Error::RowNotFound)
|
||||
.unwrap();
|
||||
let date_created = date_created_time.to_string();
|
||||
|
||||
Ok((date_created, id))
|
||||
}
|
||||
Err(_) => Err(sqlx::Error::RowNotFound),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod song_queue {
|
||||
use sqlx::Row;
|
||||
|
||||
@@ -289,6 +416,7 @@ pub mod endpoint {
|
||||
data.len()
|
||||
);
|
||||
|
||||
// TODO: Remove this
|
||||
// Save the file to disk
|
||||
let mut file = std::fs::File::create(&file_name).unwrap();
|
||||
file.write_all(&data).unwrap();
|
||||
@@ -456,4 +584,59 @@ pub mod endpoint {
|
||||
(axum::http::StatusCode::NOT_FOUND, axum::Json(response))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_metadata(
|
||||
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
|
||||
axum::Json(payload): axum::Json<super::request::create_metadata::Request>,
|
||||
) -> (
|
||||
axum::http::StatusCode,
|
||||
axum::Json<super::response::create_metadata::Response>,
|
||||
) {
|
||||
let mut response = super::response::create_metadata::Response::default();
|
||||
|
||||
if payload.is_valid() {
|
||||
let mut song = payload.to_song();
|
||||
song.filename =
|
||||
song.generate_filename(icarus_models::types::MusicTypes::FlacExtension, true);
|
||||
song.directory = crate::environment::get_root_directory().await.unwrap();
|
||||
|
||||
match song_queue::get_data(&pool, &payload.song_queue_id).await {
|
||||
Ok(data) => {
|
||||
song.data = data;
|
||||
let dir = std::path::Path::new(&song.directory);
|
||||
let save_path = dir.join(&song.filename);
|
||||
|
||||
let mut file = std::fs::File::create(&save_path).unwrap();
|
||||
file.write_all(&song.data).unwrap();
|
||||
|
||||
match song.song_path() {
|
||||
Ok(_) => match super::song_db::insert(&pool, &song).await {
|
||||
Ok((date_created, id)) => {
|
||||
song.id = id;
|
||||
song.date_created = date_created;
|
||||
response.data.push(song);
|
||||
|
||||
(axum::http::StatusCode::OK, axum::Json(response))
|
||||
}
|
||||
Err(err) => {
|
||||
response.message = format!("{:?} song {:?}", err.to_string(), song);
|
||||
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
response.message = err.to_string();
|
||||
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
response.message = err.to_string();
|
||||
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
response.message = String::from("Request body is not valid");
|
||||
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
pub async fn get_db_url() -> String {
|
||||
dotenvy::dotenv().ok();
|
||||
std::env::var(crate::keys::DBURL).expect(crate::keys::error::ERROR)
|
||||
}
|
||||
|
||||
pub async fn get_root_directory() -> Result<String, std::env::VarError> {
|
||||
dotenvy::dotenv().ok();
|
||||
std::env::var(crate::keys::ROOT_DIRECTORY)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub const DBURL: &str = "DATABASE_URL";
|
||||
pub const ROOT_DIRECTORY: &str = "ROOT_DIRECTORY";
|
||||
|
||||
pub mod error {
|
||||
pub const ERROR: &str = "DATABASE_URL must be set in .env";
|
||||
}
|
||||
+170
-59
@@ -1,24 +1,17 @@
|
||||
pub mod callers;
|
||||
pub mod environment;
|
||||
pub mod keys;
|
||||
|
||||
pub mod db {
|
||||
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use std::env;
|
||||
|
||||
pub mod connection_settings {
|
||||
pub const MAXCONN: u32 = 10;
|
||||
}
|
||||
|
||||
pub mod keys {
|
||||
pub const DBURL: &str = "DATABASE_URL";
|
||||
|
||||
pub mod error {
|
||||
pub const ERROR: &str = "DATABASE_URL must be set in .env";
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_pool() -> Result<sqlx::PgPool, sqlx::Error> {
|
||||
let database_url = get_db_url().await;
|
||||
let database_url = crate::environment::get_db_url().await;
|
||||
println!("Database url: {:?}", database_url);
|
||||
|
||||
PgPoolOptions::new()
|
||||
@@ -27,11 +20,6 @@ pub mod db {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_db_url() -> String {
|
||||
dotenvy::dotenv().ok();
|
||||
env::var(keys::DBURL).expect(keys::error::ERROR)
|
||||
}
|
||||
|
||||
pub async fn migrations(pool: &sqlx::PgPool) {
|
||||
// Run migrations using the sqlx::migrate! macro
|
||||
// Assumes your migrations are in a ./migrations folder relative to Cargo.toml
|
||||
@@ -109,6 +97,10 @@ pub mod init {
|
||||
crate::callers::endpoints::QUEUECOVERARTLINK,
|
||||
patch(crate::callers::coverart::endpoint::link),
|
||||
)
|
||||
.route(
|
||||
crate::callers::endpoints::CREATESONG,
|
||||
post(crate::callers::song::endpoint::create_metadata),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn app() -> axum::Router {
|
||||
@@ -125,18 +117,23 @@ pub mod init {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move elsewhere
|
||||
fn get_full() -> String {
|
||||
get_address() + ":" + &get_port()
|
||||
}
|
||||
// TODO: Move elsewhere
|
||||
fn get_address() -> String {
|
||||
String::from("0.0.0.0")
|
||||
}
|
||||
|
||||
// TODO: Move elsewhere
|
||||
fn get_port() -> String {
|
||||
String::from("3000")
|
||||
}
|
||||
|
||||
// TODO: Move elsewhere
|
||||
pub const ROOT: &str = "/";
|
||||
// TODO: Move elsewhere
|
||||
// basic handler that responds with a static string
|
||||
pub async fn root() -> &'static str {
|
||||
"Hello, World!"
|
||||
@@ -157,13 +154,13 @@ mod tests {
|
||||
mod db_mgr {
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::db;
|
||||
use crate::keys;
|
||||
|
||||
pub const LIMIT: usize = 6;
|
||||
|
||||
pub async fn get_pool() -> Result<sqlx::PgPool, sqlx::Error> {
|
||||
dotenvy::dotenv().ok();
|
||||
let tm_db_url = std::env::var(db::keys::DBURL).expect("DATABASE_URL must be present");
|
||||
let tm_db_url = crate::environment::get_db_url().await;
|
||||
let tm_options = sqlx::postgres::PgConnectOptions::from_str(&tm_db_url).unwrap();
|
||||
sqlx::PgPool::connect_with(tm_options).await
|
||||
}
|
||||
@@ -176,8 +173,7 @@ mod tests {
|
||||
|
||||
pub async fn connect_to_db(db_name: &str) -> Result<sqlx::PgPool, sqlx::Error> {
|
||||
dotenvy::dotenv().ok();
|
||||
let db_url =
|
||||
std::env::var(db::keys::DBURL).expect("DATABASE_URL must be set for tests");
|
||||
let db_url = crate::environment::get_db_url().await;
|
||||
let options = sqlx::postgres::PgConnectOptions::from_str(&db_url)?.database(db_name);
|
||||
sqlx::PgPool::connect_with(options).await
|
||||
}
|
||||
@@ -206,7 +202,7 @@ mod tests {
|
||||
pub fn get_database_name() -> Result<String, Box<dyn std::error::Error>> {
|
||||
dotenvy::dotenv().ok(); // Load .env file if it exists
|
||||
|
||||
match std::env::var(db::keys::DBURL) {
|
||||
match std::env::var(keys::DBURL) {
|
||||
Ok(database_url) => {
|
||||
let parsed_url = url::Url::parse(&database_url)?;
|
||||
if parsed_url.scheme() == "postgres" || parsed_url.scheme() == "postgresql" {
|
||||
@@ -273,6 +269,22 @@ mod tests {
|
||||
app.clone().oneshot(fetch_req).await
|
||||
}
|
||||
|
||||
async fn fetch_metadata_queue_req(
|
||||
app: &axum::Router,
|
||||
id: &uuid::Uuid,
|
||||
) -> Result<axum::response::Response, std::convert::Infallible> {
|
||||
let uri = format!("{}?id={}", crate::callers::endpoints::QUEUEMETADATA, id);
|
||||
|
||||
let req = axum::http::Request::builder()
|
||||
.method(axum::http::Method::GET)
|
||||
.uri(uri)
|
||||
.header(axum::http::header::CONTENT_TYPE, "application/json")
|
||||
.body(axum::body::Body::empty())
|
||||
.unwrap();
|
||||
|
||||
app.clone().oneshot(req).await
|
||||
}
|
||||
|
||||
async fn fetch_queue_data_req(
|
||||
app: &axum::Router,
|
||||
id: &uuid::Uuid,
|
||||
@@ -312,6 +324,22 @@ mod tests {
|
||||
app.clone().oneshot(req).await
|
||||
}
|
||||
|
||||
async fn queue_metadata_req(
|
||||
app: &axum::Router,
|
||||
id: &uuid::Uuid,
|
||||
) -> Result<axum::response::Response, std::convert::Infallible> {
|
||||
let payload = payload_data(&id).await;
|
||||
|
||||
let req = axum::http::Request::builder()
|
||||
.method(axum::http::Method::POST)
|
||||
.uri(crate::callers::endpoints::QUEUEMETADATA)
|
||||
.header(axum::http::header::CONTENT_TYPE, "application/json")
|
||||
.body(axum::body::Body::from(payload.to_string()))
|
||||
.unwrap();
|
||||
|
||||
app.clone().oneshot(req).await
|
||||
}
|
||||
|
||||
async fn coverart_queue_song_queue_link_req(
|
||||
app: &axum::Router,
|
||||
coverart_id: &uuid::Uuid,
|
||||
@@ -540,20 +568,8 @@ mod tests {
|
||||
get_resp_data::<crate::callers::song::response::Response>(response).await;
|
||||
assert_eq!(false, resp.data.is_empty(), "Should not be empty");
|
||||
assert_eq!(false, resp.data[0].is_nil(), "Should not be empty");
|
||||
let new_payload = payload_data(&resp.data[0]).await;
|
||||
|
||||
match app
|
||||
.clone()
|
||||
.oneshot(
|
||||
axum::http::Request::builder()
|
||||
.method(axum::http::Method::POST)
|
||||
.uri(crate::callers::endpoints::QUEUEMETADATA)
|
||||
.header(axum::http::header::CONTENT_TYPE, "application/json")
|
||||
.body(axum::body::Body::from(new_payload.to_string()))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
match queue_metadata_req(&app, &resp.data[0]).await {
|
||||
Ok(response) => {
|
||||
let resp =
|
||||
get_resp_data::<crate::callers::song::response::Response>(response)
|
||||
@@ -599,20 +615,8 @@ mod tests {
|
||||
get_resp_data::<crate::callers::song::response::Response>(response).await;
|
||||
assert_eq!(false, resp.data.is_empty(), "Should not be empty");
|
||||
assert_eq!(false, resp.data[0].is_nil(), "Should not be empty");
|
||||
let new_payload = payload_data(&resp.data[0]).await;
|
||||
|
||||
match app
|
||||
.clone()
|
||||
.oneshot(
|
||||
axum::http::Request::builder()
|
||||
.method(axum::http::Method::POST)
|
||||
.uri(crate::callers::endpoints::QUEUEMETADATA)
|
||||
.header(axum::http::header::CONTENT_TYPE, "application/json")
|
||||
.body(axum::body::Body::from(new_payload.to_string()))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
match queue_metadata_req(&app, &resp.data[0]).await {
|
||||
Ok(response) => {
|
||||
let resp =
|
||||
get_resp_data::<crate::callers::song::response::Response>(response)
|
||||
@@ -620,20 +624,8 @@ mod tests {
|
||||
assert_eq!(false, resp.data.is_empty(), "Should not be empty");
|
||||
|
||||
let id = resp.data[0];
|
||||
let uri = format!("{}?id={}", crate::callers::endpoints::QUEUEMETADATA, id);
|
||||
|
||||
match app
|
||||
.clone()
|
||||
.oneshot(
|
||||
axum::http::Request::builder()
|
||||
.method(axum::http::Method::GET)
|
||||
.uri(uri)
|
||||
.header(axum::http::header::CONTENT_TYPE, "application/json")
|
||||
.body(axum::body::Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
match fetch_metadata_queue_req(&app, &id).await {
|
||||
Ok(response) => {
|
||||
let resp = get_resp_data::<
|
||||
crate::callers::metadata::response::fetch_metadata::Response,
|
||||
@@ -1199,4 +1191,123 @@ mod tests {
|
||||
|
||||
let _ = db_mgr::drop_database(&tm_pool, &db_name).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_song() {
|
||||
let tm_pool = db_mgr::get_pool().await.unwrap();
|
||||
let db_name = db_mgr::generate_db_name().await;
|
||||
|
||||
match db_mgr::create_database(&tm_pool, &db_name).await {
|
||||
Ok(_) => {
|
||||
println!("Success");
|
||||
}
|
||||
Err(err) => {
|
||||
assert!(false, "Error: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
let pool = db_mgr::connect_to_db(&db_name).await.unwrap();
|
||||
db::migrations(&pool).await;
|
||||
|
||||
let app = init::app(pool).await;
|
||||
|
||||
// Send request
|
||||
match song_queue_req(&app).await {
|
||||
Ok(response) => {
|
||||
let resp =
|
||||
get_resp_data::<crate::callers::song::response::Response>(response).await;
|
||||
assert_eq!(false, resp.data.is_empty(), "Should not be empty");
|
||||
assert_eq!(false, resp.data[0].is_nil(), "Should not be empty");
|
||||
|
||||
match queue_metadata_req(&app, &resp.data[0]).await {
|
||||
Ok(response) => {
|
||||
let resp =
|
||||
get_resp_data::<crate::callers::song::response::Response>(response)
|
||||
.await;
|
||||
assert_eq!(false, resp.data.is_empty(), "Should not be empty");
|
||||
|
||||
let id = resp.data[0];
|
||||
|
||||
match fetch_metadata_queue_req(&app, &id).await {
|
||||
Ok(response) => {
|
||||
let resp = get_resp_data::<
|
||||
crate::callers::metadata::response::fetch_metadata::Response,
|
||||
>(response)
|
||||
.await;
|
||||
assert_eq!(false, resp.data.is_empty(), "Data should not be empty");
|
||||
let song_q_id = resp.data[0].song_queue_id;
|
||||
|
||||
let payload = serde_json::json!({
|
||||
"title": "Power of Soul",
|
||||
"artist": "Jimmi Hendrix",
|
||||
"album_artist": "Jimmi Hendrix",
|
||||
"album": "Machine Gun",
|
||||
"genre": "Psychadelic Rock",
|
||||
"date": "2016-01-01",
|
||||
"track": 1,
|
||||
"disc": 1,
|
||||
"track_count": 11,
|
||||
"disc_count": 1,
|
||||
"duration": 330,
|
||||
"audio_type": "flac",
|
||||
"user_id": "d6e159c1-9648-4c85-81e5-52f502ff53e4",
|
||||
"song_queue_id": song_q_id
|
||||
});
|
||||
|
||||
eprintln!("Payload: {:?}", payload);
|
||||
|
||||
match app
|
||||
.clone()
|
||||
.oneshot(
|
||||
axum::http::Request::builder()
|
||||
.method(axum::http::Method::POST)
|
||||
.uri(crate::callers::endpoints::CREATESONG)
|
||||
.header(
|
||||
axum::http::header::CONTENT_TYPE,
|
||||
"application/json",
|
||||
)
|
||||
.body(axum::body::Body::from(payload.to_string()))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let resp = get_resp_data::<crate::callers::song::response::create_metadata::Response>(response).await;
|
||||
assert_eq!(
|
||||
false,
|
||||
resp.data.is_empty(),
|
||||
"No songs found, Response {:?}",
|
||||
resp
|
||||
);
|
||||
let song = &resp.data[0];
|
||||
let song_id = song.id;
|
||||
assert_eq!(
|
||||
false,
|
||||
song_id.is_nil(),
|
||||
"Song id should not be nil {:?}",
|
||||
song
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
assert!(false, "Error: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
assert!(false, "Error: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
assert!(false, "Error: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
assert!(false, "Error: {:?}", err);
|
||||
}
|
||||
};
|
||||
|
||||
let _ = db_mgr::drop_database(&tm_pool, &db_name).await;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user