diff --git a/stats/Cargo.lock b/stats/Cargo.lock index b6ec79821..c08bb95ff 100644 --- a/stats/Cargo.lock +++ b/stats/Cargo.lock @@ -103,7 +103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -117,7 +117,7 @@ dependencies = [ "prost 0.11.9", "serde", "serde_json", - "serde_with", + "serde_with 2.3.3", "tonic", ] @@ -133,7 +133,7 @@ dependencies = [ "quote", "serde", "serde_yaml", - "syn 2.0.66", + "syn 2.0.79", "thiserror", ] @@ -260,7 +260,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -432,6 +432,16 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "async-attributes" version = "1.1.2" @@ -599,7 +609,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -616,7 +626,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -784,6 +794,22 @@ dependencies = [ "piper", ] +[[package]] +name = "blockscout-client" +version = "0.1.1" +source = "git+https://github.com/blockscout/blockscout-rs/?rev=506b821#506b8219d6cb8be69e1ed373759b66e8d566c89d" +dependencies = [ + "derive-new", + "reqwest 0.12.5", + "reqwest-middleware", + "reqwest-retry", + "serde", + "serde_json_path_to_error", + "serde_with 3.9.0", + "url", + "uuid", +] + [[package]] name = "blockscout-db" version = "5.0.0" @@ -890,7 +916,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", "syn_derive", ] @@ -1032,7 +1058,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -1268,7 +1294,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -1279,7 +1305,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -1292,9 +1318,27 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core", + "parking_lot_core 0.9.10", ] +[[package]] +name = "deadpool" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490" +dependencies = [ + "async-trait", + "deadpool-runtime", + "num_cpus", + "tokio", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" + [[package]] name = "der" version = "0.7.9" @@ -1327,6 +1371,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive-new" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -1623,7 +1678,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot", + "parking_lot 0.12.3", ] [[package]] @@ -1668,7 +1723,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -1683,6 +1738,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.30" @@ -1718,8 +1779,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1758,7 +1821,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.2.6", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -1777,7 +1840,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.2.6", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -1809,6 +1872,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "hashlink" version = "0.8.4" @@ -1983,6 +2052,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -2120,12 +2190,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", + "serde", ] [[package]] @@ -2136,7 +2207,7 @@ checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -2146,6 +2217,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -2384,7 +2458,7 @@ checksum = "915f6d0a2963a27cd5205c1902f32ddfe3bc035816afd268cf88c0fc0f8d287e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -2697,7 +2771,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -2851,7 +2925,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -2866,6 +2940,17 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -2873,7 +2958,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", ] [[package]] @@ -2947,7 +3046,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -2968,7 +3067,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.2.6", + "indexmap 2.6.0", ] [[package]] @@ -2988,7 +3087,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -3093,7 +3192,7 @@ dependencies = [ "proc-macro2", "quote", "rand", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -3106,7 +3205,7 @@ dependencies = [ "proc-macro2", "quote", "rand", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -3148,7 +3247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -3163,11 +3262,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.21.1", + "toml_edit", ] [[package]] @@ -3196,9 +3295,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -3213,7 +3312,7 @@ dependencies = [ "fnv", "lazy_static", "memchr", - "parking_lot", + "parking_lot 0.12.3", "protobuf", "thiserror", ] @@ -3283,7 +3382,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -3343,9 +3442,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -3386,6 +3485,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -3406,14 +3514,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -3427,13 +3535,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.5", ] [[package]] @@ -3450,9 +3558,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "relative-path" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "rend" @@ -3526,6 +3640,7 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "native-tls", "once_cell", "percent-encoding", @@ -3546,6 +3661,54 @@ dependencies = [ "winreg 0.52.0", ] +[[package]] +name = "reqwest-middleware" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562ceb5a604d3f7c885a792d42c199fd8af239d0a51b2fa6a78aafa092452b04" +dependencies = [ + "anyhow", + "async-trait", + "http 1.1.0", + "reqwest 0.12.5", + "serde", + "thiserror", + "tower-service", +] + +[[package]] +name = "reqwest-retry" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40f342894422862af74c50e1e9601cf0931accc9c6981e5eb413c46603b616b5" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "futures", + "getrandom", + "http 1.1.0", + "hyper 1.4.1", + "parking_lot 0.11.2", + "reqwest 0.12.5", + "reqwest-middleware", + "retry-policies", + "tokio", + "tracing", + "wasm-timer", +] + +[[package]] +name = "retry-policies" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "493b4243e32d6eedd29f9a398896e35c6943a123b55eec97dcaee98310d25810" +dependencies = [ + "anyhow", + "chrono", + "rand", +] + [[package]] name = "ring" version = "0.17.8" @@ -3633,6 +3796,36 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rstest" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.79", + "unicode-ident", +] + [[package]] name = "rust-ini" version = "0.18.0" @@ -3687,9 +3880,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] @@ -3838,7 +4031,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -3896,7 +4089,7 @@ dependencies = [ "proc-macro2", "quote", "sea-bae", - "syn 2.0.66", + "syn 2.0.79", "unicode-ident", ] @@ -3960,7 +4153,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", "thiserror", ] @@ -4039,7 +4232,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -4053,6 +4246,27 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_json_path_to_error" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f2c2432eb04d880635fbf5d45cd5e619aeca7d4aff62cc1699671512db7d53" +dependencies = [ + "serde", + "serde_json", + "serde_path_to_error", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.7" @@ -4086,7 +4300,25 @@ dependencies = [ "indexmap 1.9.3", "serde", "serde_json", - "serde_with_macros", + "serde_with_macros 2.3.3", + "time", +] + +[[package]] +name = "serde_with" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.6.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros 3.9.0", "time", ] @@ -4099,7 +4331,19 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", +] + +[[package]] +name = "serde_with_macros" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.79", ] [[package]] @@ -4108,7 +4352,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.6.0", "itoa", "ryu", "serde", @@ -4278,7 +4522,7 @@ dependencies = [ "futures-util", "hashlink", "hex", - "indexmap 2.2.6", + "indexmap 2.6.0", "log", "memchr", "once_cell", @@ -4493,6 +4737,7 @@ dependencies = [ "tracing-subscriber", "tynm", "url", + "wiremock", ] [[package]] @@ -4508,7 +4753,7 @@ dependencies = [ "prost-build", "serde", "serde_json", - "serde_with", + "serde_with 3.9.0", "tonic", "tonic-build", ] @@ -4520,6 +4765,7 @@ dependencies = [ "actix-web", "anyhow", "async-trait", + "blockscout-client", "blockscout-endpoint-swagger", "blockscout-service-launcher", "bytes", @@ -4533,16 +4779,19 @@ dependencies = [ "paste", "pretty_assertions", "reqwest 0.12.5", + "rstest", "sea-orm", "serde", "serde_json", - "serde_with", + "serde_with 3.9.0", "stats", "stats-proto", "tokio", "tonic", "tracing", "tracing-subscriber", + "url", + "wiremock", ] [[package]] @@ -4587,9 +4836,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -4605,7 +4854,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -4676,7 +4925,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -4777,7 +5026,7 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", "socket2 0.5.7", @@ -4803,7 +5052,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -4869,40 +5118,29 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.16", + "toml_edit", ] [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.16" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -5002,7 +5240,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] @@ -5148,9 +5386,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" @@ -5200,6 +5438,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -5302,7 +5541,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -5336,7 +5575,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5347,6 +5586,21 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.69" @@ -5555,15 +5809,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.6.18" @@ -5593,6 +5838,30 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wiremock" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fff469918e7ca034884c7fd8f93fe27bacb7fcb599fd879df6c7b429a29b646" +dependencies = [ + "assert-json-diff", + "async-trait", + "base64 0.22.1", + "deadpool", + "futures", + "http 1.1.0", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "log", + "once_cell", + "regex", + "serde", + "serde_json", + "tokio", + "url", +] + [[package]] name = "wyz" version = "0.5.1" @@ -5634,7 +5903,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.79", ] [[package]] diff --git a/stats/Cargo.toml b/stats/Cargo.toml index 891dbc69b..0a9975403 100644 --- a/stats/Cargo.toml +++ b/stats/Cargo.toml @@ -10,8 +10,10 @@ members = [ ] [workspace.dependencies] +blockscout-client = { git = "https://github.com/blockscout/blockscout-rs/", rev = "506b821" } blockscout-service-launcher = { version = "0.13.1" } - +rstest = "0.23.0" +wiremock = "0.6.2" # todo: update version after https://github.com/chronotope/chrono/pull/1600 # and remove patch diff --git a/stats/README.md b/stats/README.md index 1bfa0ab13..15ac090be 100644 --- a/stats/README.md +++ b/stats/README.md @@ -83,7 +83,14 @@ by enabling word wrapping | `STATS__FORCE_​UPDATE_ON_START` | | Fully recalculate all charts on start | `false` | | `STATS__CONCURRENT_​START_UPDATES` | | Amount of concurrent charts update on start | `3` | | `STATS__​DEFAULT_​SCHEDULE` | | Schedule used for update groups with no config | `"0 0 1 * * * *"` | -| `STATS__LIMITS__REQUESTED_​POINTS_LIMIT` | | Maximum allowed number of requested points | `182500` | +| `STATS__LIMITS__REQUESTED_​POINTS_LIMIT` | | Maximum allowed number of requested points | `182500` | +| `STATS__BLOCKSCOUT_API_URL` | Required unless `STATS__​IGNORE_​​BLOCKSCOUT_​API_​ABSENCE` is set to `true`. | URL to Blockscout API. | `null` | +| `STATS__CONDITIONAL_​START__CHECK_PERIOD_SECS` | | Time between start condition checking (if they are not satisfied) | `5` | +| `STATS__CONDITIONAL_​START__BLOCKS_RATIO__​ENABLED` | | Enable `blocks_​ratio` threshold | `true` | +| `STATS__CONDITIONAL_​START__BLOCKS_RATIO__​THRESHOLD` | | Value for `blocks_​ratio` threshold | `0.98` | +| `STATS__CONDITIONAL_​START__INTERNAL_​TRANSACTIONS_RATIO__​ENABLED` | | Enable `internal_​transactions_​ratio` threshold | `true` | +| `STATS__CONDITIONAL_​START__INTERNAL_​TRANSACTIONS_RATIO__​THRESHOLD` | | Value for `internal_​transactions_​ratio` threshold | `0.98` | +| `STATS__IGNORE_​BLOCKSCOUT_API_ABSENCE` | | Disable requirement for blockscout api url setting. Turns off corresponding features if the api setting is not set | `false` | [anchor]: <> (anchors.envs.end.service) @@ -163,7 +170,7 @@ by enabling word wrapping | `STATS_CHARTS__​LINE_CHARTS__​__​RESOLUTIONS__YEAR` | | Enable yearly data | `true` if defined | | `STATS_CHARTS__​LINE_CHARTS__​__​TITLE` | | Displayed name of ``, e.g. `"Some line chart title"` | `null` | | `STATS_CHARTS__​LINE_CHARTS__​__​UNITS` | | Measurement units, e.g. `"{{}}"` | `null` | -| `STATS_CHARTS__​TEMPLATE_VALUES__​` | | Value to substitute instead of `{{}}`, e.g. `"some_value"` | `null` | +| `STATS_CHARTS__​TEMPLATE_VALUES__​` | | Value to substitute instead of `{{}}`, e.g. `STATS_CHARTS__​TEMPLATE_VALUES__​NATIVE_COIN_SYMBOL​="some_value"`. See full list of variables in charts config file (`charts.json`). | `null` | [anchor]: <> (anchors.envs.end.charts) diff --git a/stats/justfile b/stats/justfile index a3dc2c95f..cb7f40c5a 100644 --- a/stats/justfile +++ b/stats/justfile @@ -34,6 +34,9 @@ test-with-db *args: check-envs: VALIDATE_ONLY=true cargo run --bin env-docs-generation +generate-envs: + cargo run --bin env-docs-generation + restart-generate-entities: -just stop-postgres just start-postgres diff --git a/stats/stats-proto/Cargo.toml b/stats/stats-proto/Cargo.toml index 9acc64adc..2fa11509d 100644 --- a/stats/stats-proto/Cargo.toml +++ b/stats/stats-proto/Cargo.toml @@ -10,7 +10,7 @@ actix-web = "4" prost = "0.11" tonic = "0.8" serde = { version = "1", features = ["derive"] } -serde_with = { version = "2.0", features = ["hex", "base64"] } +serde_with = { version = "3", features = ["hex", "base64"] } async-trait = "0.1" [dev-dependencies] diff --git a/stats/stats-server/Cargo.toml b/stats/stats-server/Cargo.toml index 878eafdde..a90bffff2 100644 --- a/stats/stats-server/Cargo.toml +++ b/stats/stats-server/Cargo.toml @@ -8,9 +8,10 @@ stats = { path = "../stats" } stats-proto = { path = "../stats-proto" } async-trait = "0.1" actix-web = "4" +reqwest = "0.12" tonic = "0.8" serde = { version = "1", features = ["derive"] } -serde_with = { version = "2.0", features = ["hex", "base64"] } +serde_with = { version = "3", features = ["hex", "base64"] } bytes = "1.2" tokio = { version = "1", features = ["rt-multi-thread"] } config = "0.13" @@ -25,16 +26,19 @@ sea-orm = { version = "0.12", features = [ tracing-subscriber = { version = "0.3", features = ["env-filter"] } blockscout-service-launcher = { workspace = true, features = [ "database-0_12" ] } blockscout-endpoint-swagger = { git = "https://github.com/blockscout/blockscout-rs", rev = "0d01bdf7" } +blockscout-client = { workspace = true } cron = "0.12" convert_case = "0.6.0" itertools = "0.13.0" liquid-json = "0.5.0" serde_json = "1.0" paste = "1.0" - +url = { version = "2.5", features = ["serde"] } [dev-dependencies] stats = { path = "../stats", features = ["test-utils"] } blockscout-service-launcher = { workspace = true, features = [ "database-0_12", "test-server" ] } pretty_assertions = "1.3" reqwest = "0.12" +wiremock = { workspace = true } +rstest = { workspace = true } diff --git a/stats/stats-server/src/blockscout_waiter.rs b/stats/stats-server/src/blockscout_waiter.rs new file mode 100644 index 000000000..53924045e --- /dev/null +++ b/stats/stats-server/src/blockscout_waiter.rs @@ -0,0 +1,284 @@ +use std::time::Duration; + +use crate::settings::{Settings, StartConditionSettings, ToggleableThreshold}; + +use anyhow::Context; +use blockscout_service_launcher::launcher::ConfigSettings; +use reqwest::StatusCode; +use tokio::time::sleep; +use tracing::{info, warn}; + +fn is_retryable_code(status_code: &reqwest::StatusCode) -> bool { + matches!( + *status_code, + StatusCode::INTERNAL_SERVER_ERROR + | StatusCode::SERVICE_UNAVAILABLE + | StatusCode::GATEWAY_TIMEOUT + | StatusCode::TOO_MANY_REQUESTS + | StatusCode::IM_A_TEAPOT + ) +} + +fn is_threshold_passed( + threshold: &ToggleableThreshold, + float_value: Option, + value_name: &str, +) -> Result { + let threshold = if threshold.enabled { + threshold.threshold + } else { + return Ok(true); + }; + let value = float_value + .map(|s| s.parse::()) + .transpose() + .context(format!("Parsing `{value_name}`"))?; + let Some(value) = value else { + anyhow::bail!("Received `null` value of `{value_name}`. Can't determine indexing status.",); + }; + if value < threshold { + info!( + threshold = threshold, + current_value = value, + "Threshold for `{value_name}` is not satisfied" + ); + Ok(false) + } else { + info!( + threshold = threshold, + current_value = value, + "Threshold for `{value_name}` is satisfied" + ); + Ok(true) + } +} + +pub async fn wait_for_blockscout_indexing( + api_config: blockscout_client::Configuration, + wait_config: StartConditionSettings, +) -> Result<(), anyhow::Error> { + loop { + match blockscout_client::apis::main_page_api::get_indexing_status(&api_config).await { + Ok(result) + if is_threshold_passed( + &wait_config.blocks_ratio, + result.indexed_blocks_ratio.clone(), + "indexed_blocks_ratio", + ) + .context("check index block ratio")? + && is_threshold_passed( + &wait_config.internal_transactions_ratio, + result.indexed_internal_transactions_ratio.clone(), + "indexed_internal_transactions_ratio", + )? => + { + info!("Blockscout indexing threshold passed"); + return Ok(()); + } + Ok(_) => {} + Err(blockscout_client::Error::ResponseError(r)) if is_retryable_code(&r.status) => { + warn!("Error from indexing status endpoint: {r:?}"); + } + Err(e) => { + return Err(e).context("Requesting indexing status"); + } + } + info!( + "Blockscout is not indexed enough. Checking again in {} secs", + wait_config.check_period_secs + ); + sleep(Duration::from_secs(wait_config.check_period_secs.into())).await; + } +} + +pub async fn init_blockscout_api_client( + settings: &Settings, +) -> anyhow::Result> { + match (settings.ignore_blockscout_api_absence, &settings.blockscout_api_url) { + (_, Some(blockscout_api_url)) => Ok(Some(blockscout_client::Configuration::new(blockscout_api_url.clone()))), + (true, None) => { + info!( + "Blockscout API URL has not been provided and `IGNORE_BLOCKSCOUT_API_ABSENCE` setting is \ + set to `true`. Disabling API-related functionality." + ); + Ok(None) + } + (false, None) => anyhow::bail!( + "Blockscout API URL has not been provided. Please specify it with corresponding \ + env variable (`{0}__BLOCKSCOUT_API_URL`) or set `{0}__IGNORE_BLOCKSCOUT_API_ABSENCE=true` to disable \ + functionality depending on the API.", + Settings::SERVICE_NAME + ), + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use rstest::*; + use std::time::Duration; + use tokio::{task::JoinSet, time::error::Elapsed}; + use url::Url; + use wiremock::{ + matchers::{method, path}, + Mock, MockServer, ResponseTemplate, + }; + + use super::*; + + async fn mock_indexing_status(response: ResponseTemplate) -> MockServer { + let mock_server = MockServer::start().await; + Mock::given(method("GET")) + .and(path("/api/v2/main-page/indexing-status")) + .respond_with(response) + .mount(&mock_server) + .await; + mock_server + } + + async fn test_wait_indexing( + wait_config: StartConditionSettings, + response: ResponseTemplate, + ) -> Result, Elapsed> { + let server = mock_indexing_status(response).await; + tokio::time::timeout( + Duration::from_millis(500), + wait_for_blockscout_indexing( + blockscout_client::Configuration::new(Url::from_str(&server.uri()).unwrap()), + wait_config, + ), + ) + .await + } + + #[fixture] + fn wait_config( + #[default(0.9)] blocks: f64, + #[default(0.9)] internal_transactions: f64, + ) -> StartConditionSettings { + StartConditionSettings { + blocks_ratio: ToggleableThreshold::enabled(blocks), + internal_transactions_ratio: ToggleableThreshold::enabled(internal_transactions), + check_period_secs: 0, + } + } + + #[rstest] + #[tokio::test] + async fn wait_for_blockscout_indexing_works_with_200_response( + wait_config: StartConditionSettings, + ) { + test_wait_indexing( + wait_config.clone(), + ResponseTemplate::new(200).set_body_string( + r#"{ + "finished_indexing": true, + "finished_indexing_blocks": true, + "indexed_blocks_ratio": "1.00", + "indexed_internal_transactions_ratio": "1" + }"#, + ), + ) + .await + .expect("must not timeout") + .expect("must not error"); + + test_wait_indexing( + wait_config, + ResponseTemplate::new(200).set_body_string( + r#"{ + "finished_indexing": false, + "finished_indexing_blocks": false, + "indexed_blocks_ratio": "0.80", + "indexed_internal_transactions_ratio": "0.80" + }"#, + ), + ) + .await + .expect_err("must time out"); + } + + #[rstest] + #[tokio::test] + async fn wait_for_blockscout_indexing_works_with_slow_response( + wait_config: StartConditionSettings, + ) { + test_wait_indexing( + wait_config, + ResponseTemplate::new(200) + .set_body_string( + r#"{ + "finished_indexing": false, + "finished_indexing_blocks": false, + "indexed_blocks_ratio": "1.0", + "indexed_internal_transactions_ratio": "1.0" + }"#, + ) + .set_delay(Duration::from_millis(100)), + ) + .await + .expect("must not timeout") + .expect("must not error") + } + + #[rstest] + #[tokio::test] + async fn wait_for_blockscout_indexing_works_with_infinite_timeout( + wait_config: StartConditionSettings, + ) { + test_wait_indexing( + wait_config, + ResponseTemplate::new(200) + .set_body_string( + r#"{ + "finished_indexing": false, + "finished_indexing_blocks": false, + "indexed_blocks_ratio": "0.80", + "indexed_internal_transactions_ratio": "0.80" + }"#, + ) + .set_delay(Duration::MAX), + ) + .await + .expect_err("must time out"); + } + + #[rstest] + #[tokio::test] + async fn wait_for_blockscout_indexing_retries_with_error_codes( + wait_config: StartConditionSettings, + ) { + let mut error_servers = JoinSet::from_iter([ + test_wait_indexing(wait_config.clone(), ResponseTemplate::new(429)), + test_wait_indexing(wait_config.clone(), ResponseTemplate::new(500)), + test_wait_indexing(wait_config.clone(), ResponseTemplate::new(503)), + test_wait_indexing(wait_config.clone(), ResponseTemplate::new(504)), + ]); + #[allow(for_loops_over_fallibles)] + for server in error_servers.join_next().await { + let test_result = server.unwrap(); + test_result.expect_err("must time out"); + } + } + + #[rstest] + #[tokio::test] + async fn wait_for_blockscout_indexing_fails_with_error_codes( + wait_config: StartConditionSettings, + ) { + let mut error_servers = JoinSet::from_iter([ + test_wait_indexing(wait_config.clone(), ResponseTemplate::new(400)), + test_wait_indexing(wait_config.clone(), ResponseTemplate::new(403)), + test_wait_indexing(wait_config.clone(), ResponseTemplate::new(404)), + test_wait_indexing(wait_config.clone(), ResponseTemplate::new(405)), + ]); + #[allow(for_loops_over_fallibles)] + for server in error_servers.join_next().await { + let test_result = server.unwrap(); + test_result + .expect("must fail immediately") + .expect_err("must report error"); + } + } +} diff --git a/stats/stats-server/src/lib.rs b/stats/stats-server/src/lib.rs index a953a4190..630d1c64d 100644 --- a/stats/stats-server/src/lib.rs +++ b/stats/stats-server/src/lib.rs @@ -1,3 +1,4 @@ +pub mod blockscout_waiter; mod config; mod health; mod read_service; diff --git a/stats/stats-server/src/server.rs b/stats/stats-server/src/server.rs index 20f4af391..d981cd5cb 100644 --- a/stats/stats-server/src/server.rs +++ b/stats/stats-server/src/server.rs @@ -1,6 +1,7 @@ use std::{path::PathBuf, sync::Arc, time::Duration}; use crate::{ + blockscout_waiter::{init_blockscout_api_client, wait_for_blockscout_indexing}, config::{read_charts_config, read_layout_config, read_update_groups_config}, health::HealthService, read_service::ReadService, @@ -99,6 +100,13 @@ pub async fn stats(settings: Settings) -> Result<(), anyhow::Error> { .await?; } + let blockscout_api_config = init_blockscout_api_client(&settings).await?; + + // Wait for blockscout to index, if necessary. + if let Some(config) = blockscout_api_config { + wait_for_blockscout_indexing(config, settings.conditional_start).await?; + } + let update_service = Arc::new(UpdateService::new(db.clone(), blockscout, charts.clone()).await?); diff --git a/stats/stats-server/src/settings.rs b/stats/stats-server/src/settings.rs index afa03cbaa..addcd9485 100644 --- a/stats/stats-server/src/settings.rs +++ b/stats/stats-server/src/settings.rs @@ -10,18 +10,26 @@ use serde_with::{serde_as, DisplayFromStr}; use std::{net::SocketAddr, path::PathBuf, str::FromStr}; #[serde_as] -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(default, deny_unknown_fields)] pub struct Settings { pub db_url: String, pub create_database: bool, pub run_migrations: bool, pub blockscout_db_url: String, + /// Blockscout API url. + /// + /// Required. To launch without it api use [`Settings::ignore_blockscout_api_absence`]. + pub blockscout_api_url: Option, + /// Disable functionality that utilizes [`Settings::blockscout_api_url`] if the parameter + /// is not provided. By default the url is required to not silently suppress such features. + pub ignore_blockscout_api_absence: bool, #[serde_as(as = "DisplayFromStr")] pub default_schedule: Schedule, pub force_update_on_start: Option, // None = no update pub concurrent_start_updates: usize, pub limits: LimitsSettings, + pub conditional_start: StartConditionSettings, pub charts_config: PathBuf, pub layout_config: PathBuf, pub update_groups_config: PathBuf, @@ -54,11 +62,14 @@ impl Default for Settings { force_update_on_start: Some(false), concurrent_start_updates: 3, limits: Default::default(), + conditional_start: Default::default(), charts_config: PathBuf::from_str("config/charts.json").unwrap(), layout_config: PathBuf::from_str("config/layout.json").unwrap(), update_groups_config: PathBuf::from_str("config/update_groups.json").unwrap(), swagger_file: PathBuf::from("../stats-proto/swagger/stats.swagger.yaml"), blockscout_db_url: Default::default(), + blockscout_api_url: None, + ignore_blockscout_api_absence: false, create_database: Default::default(), run_migrations: Default::default(), metrics: Default::default(), @@ -89,6 +100,88 @@ impl Default for LimitsSettings { } } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(default, deny_unknown_fields)] +pub struct StartConditionSettings { + pub blocks_ratio: ToggleableThreshold, + pub internal_transactions_ratio: ToggleableThreshold, + pub check_period_secs: u32, +} + +impl Default for StartConditionSettings { + fn default() -> Self { + Self { + // in some networks it's always almost 1 + blocks_ratio: ToggleableThreshold::default(), + internal_transactions_ratio: ToggleableThreshold::default(), + check_period_secs: 5, + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(default, deny_unknown_fields)] +pub struct ToggleableThreshold { + pub enabled: bool, + pub threshold: f64, +} + +impl ToggleableThreshold { + pub fn disabled() -> Self { + Self { + enabled: false, + threshold: 0.0, + } + } + + pub fn enabled(value: f64) -> Self { + Self { + enabled: true, + threshold: value, + } + } + + pub fn set_threshold(mut self, value: f64) -> Self { + self.threshold = value; + self + } + + pub fn set_disabled(mut self) -> Self { + self.enabled = false; + self + } +} + +impl Default for ToggleableThreshold { + fn default() -> Self { + Self::enabled(0.98) + } +} + impl ConfigSettings for Settings { const SERVICE_NAME: &'static str = "STATS"; } + +#[cfg(test)] +mod tests { + use crate::config_env::test_utils::check_envs_parsed_to; + + use super::*; + + #[test] + fn start_condition_thresholds_can_be_disabled_with_envs() { + check_envs_parsed_to( + "START_SETTINGS", + [( + "START_SETTINGS__BLOCKS_RATIO__ENABLED".to_owned(), + "false".to_owned(), + )] + .into(), + StartConditionSettings { + blocks_ratio: ToggleableThreshold::default().set_disabled(), + ..StartConditionSettings::default() + }, + ) + .unwrap() + } +} diff --git a/stats/stats-server/tests/it/counters.rs b/stats/stats-server/tests/it/counters.rs index 035b34fe5..b6ae7cdfb 100644 --- a/stats/stats-server/tests/it/counters.rs +++ b/stats/stats-server/tests/it/counters.rs @@ -4,7 +4,10 @@ use blockscout_service_launcher::{ }; use chrono::NaiveDate; -use stats::tests::{init_db::init_db_all, mock_blockscout::fill_mock_blockscout_data}; +use stats::tests::{ + init_db::init_db_all, + mock_blockscout::{fill_mock_blockscout_data, mock_blockscout_api}, +}; use stats_proto::blockscout::stats::v1::Counters; use stats_server::{stats, Settings}; @@ -14,6 +17,7 @@ use std::{collections::HashSet, path::PathBuf, str::FromStr}; #[ignore = "needs database"] async fn test_counters_ok() { let (stats_db, blockscout_db) = init_db_all("test_counters_ok").await; + let blockscout_api = mock_blockscout_api().await; fill_mock_blockscout_data(&blockscout_db, NaiveDate::from_str("2023-03-01").unwrap()).await; std::env::set_var("STATS__CONFIG", "./tests/config/test.toml"); @@ -25,6 +29,7 @@ async fn test_counters_ok() { settings.update_groups_config = PathBuf::from_str("../config/update_groups.json").unwrap(); settings.db_url = stats_db.db_url(); settings.blockscout_db_url = blockscout_db.db_url(); + settings.blockscout_api_url = Some(url::Url::from_str(&blockscout_api.uri()).unwrap()); init_server(|| stats(settings), &base).await; diff --git a/stats/stats-server/tests/it/lines.rs b/stats/stats-server/tests/it/lines.rs index 1088257f0..f8cea1660 100644 --- a/stats/stats-server/tests/it/lines.rs +++ b/stats/stats-server/tests/it/lines.rs @@ -5,7 +5,10 @@ use blockscout_service_launcher::{ use chrono::NaiveDate; use stats::{ - tests::{init_db::init_db_all, mock_blockscout::fill_mock_blockscout_data}, + tests::{ + init_db::init_db_all, + mock_blockscout::{fill_mock_blockscout_data, mock_blockscout_api}, + }, ResolutionKind, }; use stats_server::{stats, Settings}; @@ -16,6 +19,7 @@ use std::{collections::HashMap, path::PathBuf, str::FromStr}; #[ignore = "needs database"] async fn test_lines_ok() { let (stats_db, blockscout_db) = init_db_all("test_lines_ok").await; + let blockscout_api = mock_blockscout_api().await; fill_mock_blockscout_data(&blockscout_db, NaiveDate::from_str("2023-03-01").unwrap()).await; std::env::set_var("STATS__CONFIG", "./tests/config/test.toml"); @@ -27,6 +31,7 @@ async fn test_lines_ok() { settings.update_groups_config = PathBuf::from_str("../config/update_groups.json").unwrap(); settings.db_url = stats_db.db_url(); settings.blockscout_db_url = blockscout_db.db_url(); + settings.blockscout_api_url = Some(url::Url::from_str(&blockscout_api.uri()).unwrap()); init_server(|| stats(settings), &base).await; diff --git a/stats/stats-server/tests/it/swagger.rs b/stats/stats-server/tests/it/swagger.rs index 2414f4b64..7fb27c85b 100644 --- a/stats/stats-server/tests/it/swagger.rs +++ b/stats/stats-server/tests/it/swagger.rs @@ -4,7 +4,7 @@ use blockscout_service_launcher::{ }; use pretty_assertions::assert_eq; -use stats::tests::init_db::init_db_all; +use stats::tests::{init_db::init_db_all, mock_blockscout::mock_blockscout_api}; use stats_server::{stats, Settings}; use std::{path::PathBuf, str::FromStr}; @@ -15,6 +15,8 @@ use crate::common::send_arbitrary_request; #[ignore = "needs database"] async fn test_swagger_ok() { let (stats_db, blockscout_db) = init_db_all("test_swagger_ok").await; + let blockscout_api = mock_blockscout_api().await; + std::env::set_var("STATS__CONFIG", "./tests/config/test.toml"); let mut settings = Settings::build().expect("Failed to build settings"); let (server_settings, base) = get_test_server_settings(); @@ -24,6 +26,7 @@ async fn test_swagger_ok() { settings.update_groups_config = PathBuf::from_str("../config/update_groups.json").unwrap(); settings.db_url = stats_db.db_url(); settings.blockscout_db_url = blockscout_db.db_url(); + settings.blockscout_api_url = Some(url::Url::from_str(&blockscout_api.uri()).unwrap()); init_server(|| stats(settings), &base).await; diff --git a/stats/stats/Cargo.toml b/stats/stats/Cargo.toml index 066e136e1..a99278374 100644 --- a/stats/stats/Cargo.toml +++ b/stats/stats/Cargo.toml @@ -33,11 +33,9 @@ rust_decimal = "1.27" pretty_assertions = { version= "1.2", optional = true } tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true } blockscout-service-launcher = { version = "0.13.1", features = [ "database-0_12", "test-database" ], optional = true } +wiremock = { workspace = true, optional = true } [dev-dependencies] -pretty_assertions = "1.2" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } -blockscout-service-launcher = { version = "0.13.1", features = [ "database-0_12", "test-database" ] } sea-orm = { version = "0.12", features = [ "sqlx-postgres", "sqlx-sqlite", @@ -46,6 +44,12 @@ sea-orm = { version = "0.12", features = [ rust_decimal = "1.27" rust_decimal_macros = "1.27" +# test-utils +pretty_assertions = "1.2" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +blockscout-service-launcher = { version = "0.13.1", features = [ "database-0_12", "test-database" ] } +wiremock = { workspace = true } + [features] default = [] -test-utils = ["dep:tracing-subscriber", "dep:blockscout-service-launcher", "dep:pretty_assertions"] +test-utils = ["dep:tracing-subscriber", "dep:blockscout-service-launcher", "dep:pretty_assertions", "dep:wiremock"] diff --git a/stats/stats/src/tests/mock_blockscout.rs b/stats/stats/src/tests/mock_blockscout.rs index ce4b84e64..7407a0dbd 100644 --- a/stats/stats/src/tests/mock_blockscout.rs +++ b/stats/stats/src/tests/mock_blockscout.rs @@ -1,3 +1,5 @@ +#![cfg(any(feature = "test-utils", test))] + use blockscout_db::entity::{ address_coin_balances_daily, addresses, block_rewards, blocks, internal_transactions, migrations_status, smart_contracts, tokens, transactions, @@ -6,6 +8,27 @@ use chrono::{NaiveDate, NaiveDateTime}; use rand::{Rng, SeedableRng}; use sea_orm::{prelude::Decimal, ActiveValue::NotSet, DatabaseConnection, EntityTrait, Set}; use std::str::FromStr; +use wiremock::{ + matchers::{method, path}, + Mock, MockServer, ResponseTemplate, +}; + +pub async fn mock_blockscout_api() -> MockServer { + let mock_server = MockServer::start().await; + Mock::given(method("GET")) + .and(path("/api/v2/main-page/indexing-status")) + .respond_with(ResponseTemplate::new(200).set_body_string( + r#"{ + "finished_indexing": true, + "finished_indexing_blocks": true, + "indexed_blocks_ratio": "1.00", + "indexed_internal_transactions_ratio": "1.00" + }"#, + )) + .mount(&mock_server) + .await; + mock_server +} pub async fn fill_mock_blockscout_data(blockscout: &DatabaseConnection, max_date: NaiveDate) { addresses::Entity::insert_many([