-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat : added snos worker implementation and unit tests #16
Changes from all commits
2e9cf16
10ba1e8
2c80f7d
32e9584
e56b3da
79e8efb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ use async_trait::async_trait; | |
use color_eyre::eyre::eyre; | ||
use color_eyre::Result; | ||
use mongodb::bson::Document; | ||
use mongodb::options::UpdateOptions; | ||
use mongodb::options::{FindOneOptions, UpdateOptions}; | ||
use mongodb::{ | ||
bson::doc, | ||
options::{ClientOptions, ServerApi, ServerApiVersion}, | ||
|
@@ -115,4 +115,16 @@ impl Database for MongoDb { | |
self.update_job_optimistically(job, update).await?; | ||
Ok(()) | ||
} | ||
|
||
async fn get_latest_job_by_type_and_internal_id(&self, job_type: JobType) -> Result<Option<JobItem>> { | ||
let filter = doc! { | ||
"job_type": mongodb::bson::to_bson(&job_type)?, | ||
}; | ||
let find_options = FindOneOptions::builder().sort(doc! { "internal_id": -1 }).build(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAICS internal ID is a string without additional restrictions https://github.com/unstark/madara-orchestrator/blob/3fe37331183cf3291aaef9d1e2664e54e86f0648/crates/orchestrator/src/jobs/types.rs#L104, so it's not necessarily a unique incrementing integer. We might need an additional field, e.g. created_at in microseconds to achieve what you want There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So internal_id would actually be the block number but in String. The reason it was made a string is that jobs use this column to uniquely identify a job based on the type. Like, I want the SNOS run of block 6. Now jobs can have any sort of internal id (number, uuid etc.). So as a generic, a string param was used here. I am not sure if this is the best approach though but here, -1 should work because it's a number represented as string? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok nvm, this is incorrect. This function can be used by any job so if some job has a different form of internal id, this will break. However, the created_at might not be the best check either because what if we create jobs in parallel somewhere or after receiving something from a queue, there's no guarantee that the jobs would be created sequentially. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now, we can explicitly rename this function to say There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. function name changed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would work (renaming), as for the created_at - even if there is a collision, the function would still do its job :) |
||
Ok(self | ||
.get_job_collection() | ||
.find_one(filter, find_options) | ||
.await | ||
.expect("Failed to fetch latest job by given job type")) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,4 @@ pub mod server; | |
pub mod queue; | ||
|
||
pub mod common; | ||
mod workers; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
use crate::config::config_force_init; | ||
use crate::database::MockDatabase; | ||
use crate::jobs::types::{ExternalId, JobItem, JobStatus, JobType}; | ||
use crate::queue::MockQueueProvider; | ||
use crate::tests::common::init_config; | ||
use crate::workers::snos::SnosWorker; | ||
use crate::workers::Worker; | ||
use da_client_interface::MockDaClient; | ||
use httpmock::MockServer; | ||
use mockall::predicate::eq; | ||
use rstest::rstest; | ||
use serde_json::json; | ||
use std::collections::HashMap; | ||
use std::error::Error; | ||
use uuid::Uuid; | ||
|
||
#[rstest] | ||
#[case(false)] | ||
#[case(true)] | ||
#[tokio::test] | ||
async fn test_snos_worker(#[case] db_val: bool) -> Result<(), Box<dyn Error>> { | ||
let server = MockServer::start(); | ||
let da_client = MockDaClient::new(); | ||
let mut db = MockDatabase::new(); | ||
let mut queue = MockQueueProvider::new(); | ||
let start_job_index; | ||
let block; | ||
|
||
const JOB_PROCESSING_QUEUE: &str = "madara_orchestrator_job_processing_queue"; | ||
|
||
// Mocking db function expectations | ||
if !db_val { | ||
db.expect_get_latest_job_by_type_and_internal_id().times(1).with(eq(JobType::SnosRun)).returning(|_| Ok(None)); | ||
start_job_index = 1; | ||
block = 5; | ||
} else { | ||
let uuid_temp = Uuid::new_v4(); | ||
|
||
db.expect_get_latest_job_by_type_and_internal_id() | ||
.with(eq(JobType::SnosRun)) | ||
.returning(move |_| Ok(Some(get_job_item_mock_by_id("1".to_string(), uuid_temp)))); | ||
block = 6; | ||
start_job_index = 2; | ||
} | ||
|
||
for i in start_job_index..block + 1 { | ||
// Getting jobs for check expectations | ||
db.expect_get_job_by_internal_id_and_type() | ||
.times(1) | ||
.with(eq(i.clone().to_string()), eq(JobType::SnosRun)) | ||
.returning(|_, _| Ok(None)); | ||
|
||
let uuid = Uuid::new_v4(); | ||
|
||
// creating jobs call expectations | ||
db.expect_create_job() | ||
.times(1) | ||
.withf(move |item| item.internal_id == i.clone().to_string()) | ||
.returning(move |_| Ok(get_job_item_mock_by_id(i.clone().to_string(), uuid))); | ||
} | ||
|
||
// Queue function call simulations | ||
queue | ||
.expect_send_message_to_queue() | ||
.returning(|_, _, _| Ok(())) | ||
.withf(|queue, _payload, _delay| queue == JOB_PROCESSING_QUEUE); | ||
|
||
// mock block number (madara) : 5 | ||
let rpc_response_block_number = block; | ||
let response = json!({ "id": 1,"jsonrpc":"2.0","result": rpc_response_block_number }); | ||
let config = | ||
init_config(Some(format!("http://localhost:{}", server.port())), Some(db), Some(queue), Some(da_client)).await; | ||
config_force_init(config).await; | ||
|
||
// mocking block call | ||
let rpc_block_call_mock = server.mock(|when, then| { | ||
when.path("/").body_contains("starknet_blockNumber"); | ||
then.status(200).body(serde_json::to_vec(&response).unwrap()); | ||
}); | ||
|
||
let snos_worker = SnosWorker {}; | ||
snos_worker.run_worker().await?; | ||
|
||
rpc_block_call_mock.assert(); | ||
|
||
Ok(()) | ||
} | ||
|
||
fn get_job_item_mock_by_id(id: String, uuid: Uuid) -> JobItem { | ||
JobItem { | ||
id: uuid, | ||
internal_id: id.clone(), | ||
job_type: JobType::SnosRun, | ||
status: JobStatus::Created, | ||
external_id: ExternalId::Number(0), | ||
metadata: HashMap::new(), | ||
version: 0, | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
use crate::workers::Worker; | ||
use async_trait::async_trait; | ||
use std::error::Error; | ||
|
||
pub struct ProvingWorker; | ||
|
||
#[async_trait] | ||
impl Worker for ProvingWorker { | ||
/// 1. Fetch all successful SNOS job runs that don't have a proving job | ||
/// 2. Create a proving job for each SNOS job run | ||
async fn run_worker(&self) { | ||
async fn run_worker(&self) -> Result<(), Box<dyn Error>> { | ||
todo!() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's only add this code if the test flag is enabled
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it could work without the global config? Apart from the need to carry it through all methods, are there any reasons to use static initialization?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya that's the only advantage. Otherwise we need to pass it everywhere although that does make testing easier. Do you think we should do the latter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be fair, yes :) that would be much easier to test, also you'd be able to pass only necessary parts of the config instead of the entire struct