Skip to content

Commit 2028c2b

Browse files
committed
add try all endpoints tests
1 parent e620216 commit 2028c2b

File tree

1 file changed

+168
-35
lines changed

1 file changed

+168
-35
lines changed

pallets/drand/src/tests.rs

Lines changed: 168 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616

1717
use crate::{
1818
mock::*, BeaconConfig, BeaconConfigurationPayload, BeaconInfoResponse, Call, DrandResponseBody,
19-
Error, Pulse, Pulses, PulsesPayload, RoundNumber,
19+
Error, Pulse, Pulses, PulsesPayload, ENDPOINTS, QUICKNET_CHAIN_HASH,
2020
};
2121
use codec::Encode;
2222
use frame_support::{
2323
assert_noop, assert_ok,
2424
pallet_prelude::{InvalidTransaction, TransactionSource},
2525
};
2626
use frame_system::RawOrigin;
27+
use serde_json;
2728
use sp_runtime::{
2829
offchain::{
2930
testing::{PendingRequest, TestOffchainExt},
@@ -38,6 +39,7 @@ pub const ROUND_NUMBER: u64 = 1000;
3839
// Quicknet parameters
3940
pub const DRAND_PULSE: &str = "{\"round\":1000,\"randomness\":\"fe290beca10872ef2fb164d2aa4442de4566183ec51c56ff3cd603d930e54fdd\",\"signature\":\"b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39\"}";
4041
pub const DRAND_INFO_RESPONSE: &str = "{\"public_key\":\"83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a\",\"period\":3,\"genesis_time\":1692803367,\"hash\":\"52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971\",\"groupHash\":\"f477d5c89f21a17c863a7f937c6a6d15859414d2be09cd448d4279af331c5d3e\",\"schemeID\":\"bls-unchained-g1-rfc9380\",\"metadata\":{\"beaconID\":\"quicknet\"}}";
42+
const INVALID_JSON: &str = r#"{"round":1000,"randomness":"not base64??","signature":}"#;
4143

4244
#[test]
4345
fn it_can_submit_valid_pulse_when_beacon_config_exists() {
@@ -342,53 +344,45 @@ fn test_not_validate_unsigned_write_pulse_with_no_payload_signature() {
342344
}
343345

344346
#[test]
345-
#[ignore]
346-
fn test_validate_unsigned_write_pulse_by_non_authority() {
347-
// TODO: https://github.com/ideal-lab5/pallet-drand/issues/3
348-
todo!(
349-
"the transaction should be validated even if the signer of the payload is not an authority"
350-
);
351-
}
347+
fn can_execute_and_handle_valid_http_responses() {
348+
use serde_json;
352349

353-
#[test]
354-
#[ignore]
355-
fn test_not_validate_unsigned_set_beacon_config_by_non_authority() {
356-
// TODO: https://github.com/ideal-lab5/pallet-drand/issues/3
357-
todo!(
358-
"the transaction should not be validated if the signer of the payload is not an authority"
359-
);
360-
}
350+
let expected_pulse: DrandResponseBody = serde_json::from_str(DRAND_PULSE).unwrap();
361351

362-
#[test]
363-
fn can_execute_and_handle_valid_http_responses() {
364352
let (offchain, state) = TestOffchainExt::new();
365353
let mut t = sp_io::TestExternalities::default();
366354
t.register_extension(OffchainWorkerExt::new(offchain));
367355

368356
{
369357
let mut state = state.write();
370-
state.expect_request(PendingRequest {
371-
method: "GET".into(),
372-
uri: "https://drand.cloudflare.com/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971/public/1".into(),
373-
response: Some(DRAND_PULSE.as_bytes().to_vec()),
374-
sent: true,
375-
..Default::default()
376-
});
377-
state.expect_request(PendingRequest {
378-
method: "GET".into(),
379-
uri: "https://drand.cloudflare.com/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971/public/latest".into(),
380-
response: Some(DRAND_PULSE.as_bytes().to_vec()),
381-
sent: true,
382-
..Default::default()
383-
});
358+
359+
for endpoint in ENDPOINTS.iter() {
360+
state.expect_request(PendingRequest {
361+
method: "GET".into(),
362+
uri: format!("{}/{}/public/1000", endpoint, QUICKNET_CHAIN_HASH),
363+
response: Some(DRAND_PULSE.as_bytes().to_vec()),
364+
sent: true,
365+
..Default::default()
366+
});
367+
}
368+
369+
for endpoint in ENDPOINTS.iter() {
370+
state.expect_request(PendingRequest {
371+
method: "GET".into(),
372+
uri: format!("{}/{}/public/latest", endpoint, QUICKNET_CHAIN_HASH),
373+
response: Some(DRAND_PULSE.as_bytes().to_vec()),
374+
sent: true,
375+
..Default::default()
376+
});
377+
}
384378
}
385379

386380
t.execute_with(|| {
387-
let actual_specific = Drand::fetch_drand_by_round(RoundNumber::from(1u64)).unwrap();
388-
assert_eq!(actual_specific, DRAND_PULSE);
381+
let actual_specific = Drand::fetch_drand_by_round(1000u64).unwrap();
382+
assert_eq!(actual_specific, expected_pulse);
389383

390384
let actual_pulse = Drand::fetch_drand_latest().unwrap();
391-
assert_eq!(actual_pulse, DRAND_PULSE);
385+
assert_eq!(actual_pulse, expected_pulse);
392386
});
393387
}
394388

@@ -417,3 +411,142 @@ fn validate_unsigned_rejects_future_block_number() {
417411
assert_noop!(validity, InvalidTransaction::Future);
418412
});
419413
}
414+
415+
#[test]
416+
fn test_all_endpoints_fail() {
417+
let (offchain, state) = TestOffchainExt::new();
418+
let mut t = sp_io::TestExternalities::default();
419+
t.register_extension(OffchainWorkerExt::new(offchain));
420+
421+
{
422+
let mut state = state.write();
423+
let endpoints = ENDPOINTS;
424+
425+
for endpoint in endpoints.iter() {
426+
state.expect_request(PendingRequest {
427+
method: "GET".into(),
428+
uri: format!("{}/{}/public/1000", endpoint, QUICKNET_CHAIN_HASH),
429+
response: Some(INVALID_JSON.as_bytes().to_vec()),
430+
sent: true,
431+
..Default::default()
432+
});
433+
}
434+
}
435+
436+
t.execute_with(|| {
437+
let result = Drand::fetch_drand_by_round(1000u64);
438+
assert!(
439+
result.is_err(),
440+
"All endpoints should fail due to invalid JSON responses"
441+
);
442+
});
443+
}
444+
445+
#[test]
446+
fn test_eventual_success() {
447+
let expected_pulse: DrandResponseBody = serde_json::from_str(DRAND_PULSE).unwrap();
448+
449+
let (offchain, state) = TestOffchainExt::new();
450+
let mut t = sp_io::TestExternalities::default();
451+
t.register_extension(OffchainWorkerExt::new(offchain));
452+
453+
{
454+
let mut state = state.write();
455+
let endpoints = ENDPOINTS;
456+
457+
// We'll make all endpoints except the last return invalid JSON.
458+
// Since no meta is provided, these are "200 OK" but invalid JSON, causing decode failures.
459+
// The last endpoint returns the valid DRAND_PULSE JSON, leading to success.
460+
461+
// Endpoint 0: Invalid JSON (decode fail)
462+
state.expect_request(PendingRequest {
463+
method: "GET".into(),
464+
uri: format!("{}/{}/public/1000", endpoints[0], QUICKNET_CHAIN_HASH),
465+
response: Some(INVALID_JSON.as_bytes().to_vec()),
466+
sent: true,
467+
..Default::default()
468+
});
469+
470+
// Endpoint 1: Invalid JSON
471+
state.expect_request(PendingRequest {
472+
method: "GET".into(),
473+
uri: format!("{}/{}/public/1000", endpoints[1], QUICKNET_CHAIN_HASH),
474+
response: Some(Vec::new()),
475+
sent: true,
476+
..Default::default()
477+
});
478+
479+
// Endpoint 2: Invalid JSON
480+
state.expect_request(PendingRequest {
481+
method: "GET".into(),
482+
uri: format!("{}/{}/public/1000", endpoints[2], QUICKNET_CHAIN_HASH),
483+
response: Some(INVALID_JSON.as_bytes().to_vec()),
484+
sent: true,
485+
..Default::default()
486+
});
487+
488+
// Endpoint 3: Invalid JSON
489+
state.expect_request(PendingRequest {
490+
method: "GET".into(),
491+
uri: format!("{}/{}/public/1000", endpoints[3], QUICKNET_CHAIN_HASH),
492+
response: Some(INVALID_JSON.as_bytes().to_vec()),
493+
sent: true,
494+
..Default::default()
495+
});
496+
497+
// Endpoint 4: Valid JSON (success)
498+
state.expect_request(PendingRequest {
499+
method: "GET".into(),
500+
uri: format!("{}/{}/public/1000", endpoints[4], QUICKNET_CHAIN_HASH),
501+
response: Some(DRAND_PULSE.as_bytes().to_vec()),
502+
sent: true,
503+
..Default::default()
504+
});
505+
}
506+
507+
t.execute_with(|| {
508+
let actual = Drand::fetch_drand_by_round(1000u64).unwrap();
509+
assert_eq!(
510+
actual, expected_pulse,
511+
"Should succeed on the last endpoint after failing at the previous ones"
512+
);
513+
});
514+
}
515+
516+
#[test]
517+
fn test_invalid_json_then_success() {
518+
let expected_pulse: DrandResponseBody = serde_json::from_str(DRAND_PULSE).unwrap();
519+
520+
let (offchain, state) = TestOffchainExt::new();
521+
let mut t = sp_io::TestExternalities::default();
522+
t.register_extension(OffchainWorkerExt::new(offchain));
523+
524+
{
525+
let mut state = state.write();
526+
527+
let endpoints = ENDPOINTS;
528+
529+
// Endpoint 1: Invalid JSON
530+
state.expect_request(PendingRequest {
531+
method: "GET".into(),
532+
uri: format!("{}/{}/public/1000", endpoints[0], QUICKNET_CHAIN_HASH),
533+
response: Some(INVALID_JSON.as_bytes().to_vec()),
534+
sent: true,
535+
..Default::default()
536+
});
537+
538+
// Endpoint 2: Valid response
539+
state.expect_request(PendingRequest {
540+
method: "GET".into(),
541+
uri: format!("{}/{}/public/1000", endpoints[1], QUICKNET_CHAIN_HASH),
542+
response: Some(DRAND_PULSE.as_bytes().to_vec()),
543+
sent: true,
544+
..Default::default()
545+
});
546+
}
547+
548+
t.execute_with(|| {
549+
let actual = Drand::fetch_drand_by_round(1000u64).unwrap();
550+
assert_eq!(actual, expected_pulse);
551+
});
552+
}

0 commit comments

Comments
 (0)