From c972252751390b5018b551b8e62774a1fd7526d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Mon, 13 Jan 2025 00:55:22 +0100 Subject: [PATCH] Tezt/Cloud: Scenario is resilient to Tzkt API failures --- tezt/tests/cloud/dal.ml | 4 ++ tezt/tests/cloud/network.ml | 123 ++++++++++++++++++++++------------- tezt/tests/cloud/network.mli | 28 +++++--- 3 files changed, 102 insertions(+), 53 deletions(-) diff --git a/tezt/tests/cloud/dal.ml b/tezt/tests/cloud/dal.ml index 7cd72c3351bd..cddf88f7e0de 100644 --- a/tezt/tests/cloud/dal.ml +++ b/tezt/tests/cloud/dal.ml @@ -2337,6 +2337,8 @@ let init ~(configuration : configuration) etherlink_configuration cloud Network.aliases ~accounts configuration.network in let* versions = Network.versions configuration.network in + let aliases = Option.value ~default:(Hashtbl.create 0) aliases in + let versions = Option.value ~default:(Hashtbl.create 0) versions in let otel = Cloud.open_telemetry_endpoint cloud in Lwt.return { @@ -2385,6 +2387,8 @@ let update_bakers_infos t = Network.aliases ~accounts t.configuration.network in let* versions = Network.versions t.configuration.network in + let aliases = Option.value ~default:t.aliases aliases in + let versions = Option.value ~default:t.versions versions in t.aliases <- aliases ; t.versions <- versions ; Lwt.return_unit diff --git a/tezt/tests/cloud/network.ml b/tezt/tests/cloud/network.ml index 0f3873a7541c..c027446e49c4 100644 --- a/tezt/tests/cloud/network.ml +++ b/tezt/tests/cloud/network.ml @@ -63,71 +63,106 @@ let expected_pow = function `Sandbox -> 0. | _ -> 26. let versions network = match network with - | (`Mainnet | `Ghostnet) as public_network -> - let uri = - Format.sprintf - "https://api.%s.tzkt.io/v1/delegates?limit=5000&active=true" - (to_string public_network) + | (`Mainnet | `Ghostnet) as public_network -> ( + let decoder json = + json |> JSON.as_list |> List.to_seq + |> Seq.map (fun json_account -> + JSON. + ( json_account |-> "address" |> as_string, + json_account |-> "software" |-> "version" |> as_string_opt )) + |> Seq.filter_map (function + | address, None -> Some (address, "unknown") + | address, Some version -> Some (address, version)) + |> Hashtbl.of_seq in - let* output = - Process.spawn "curl" [uri] |> Process.check_and_read_stdout + let rpc = + RPC_core.( + make + ~query_string:[("limit", "5000"); ("active", "true")] + GET + ["v1"; "delegates"] + decoder) in - let json_accounts = - JSON.parse ~origin:"Network.versions" output |> JSON.as_list + let endpoint = + Endpoint. + { + host = Format.asprintf "api.%s.tzkt.io" (to_string public_network); + scheme = "https"; + port = 443; + } in - json_accounts |> List.to_seq - |> Seq.map (fun json_account -> - JSON. - ( json_account |-> "address" |> as_string, - json_account |-> "software" |-> "version" |> as_string_opt )) - |> Seq.filter_map (function - | address, None -> Some (address, "unknown") - | address, Some version -> Some (address, version)) - |> Hashtbl.of_seq |> Lwt.return + let* response = RPC_core.call_raw endpoint rpc in + try + RPC_core.decode_raw ~origin:"Network.versions" rpc response.body + |> Lwt.return_some + with exn -> + Log.warn + "Unexpected error while fetching versions (code: %d): '%s'" + response.code + (Printexc.to_string exn) ; + Lwt.return_none) | `Weeklynet _ -> (* No easy way to get this information. *) - Lwt.return (Hashtbl.create 0) + Lwt.return_some (Hashtbl.create 0) | `Sandbox -> (* Not sure what to do here since it depends on the docker image. We can figure that out later. *) - Lwt.return (Hashtbl.create 0) + Lwt.return_some (Hashtbl.create 0) let delegates ?(accounts = []) network = match network with - | (`Mainnet | `Ghostnet) as network -> - let uri = - Format.sprintf - "https://api.%s.tzkt.io/v1/delegates?limit=5000&active=true" - (to_string network) + | (`Mainnet | `Ghostnet) as network -> ( + let decoder json = + json |> JSON.as_list + |> List.map (fun json_account -> + JSON. + ( json_account |-> "alias" |> as_string_opt, + json_account |-> "address" |> as_string, + json_account |-> "publicKey" |> as_string )) in - let* output = - Process.spawn ~log_output:false "curl" [uri] - |> Process.check_and_read_stdout + let rpc = + RPC_core.( + make + ~query_string:[("limit", "5000"); ("active", "true")] + GET + ["v1"; "delegates"] + decoder) in - let json_accounts = - JSON.parse ~origin:"Network.aliases" output |> JSON.as_list + let endpoint = + Endpoint. + { + host = Format.asprintf "api.%s.tzkt.io" (to_string network); + scheme = "https"; + port = 443; + } in - json_accounts - |> List.map (fun json_account -> - JSON. - ( json_account |-> "alias" |> as_string_opt, - json_account |-> "address" |> as_string, - json_account |-> "publicKey" |> as_string )) - |> Lwt.return + let* response = RPC_core.call_raw endpoint rpc in + try + RPC_core.decode_raw ~origin:"Network.aliases" rpc response.body + |> Lwt.return_some + with exn -> + Log.warn + "Unexpected error while fetching aliases (code: %d): '%s'" + response.code + (Printexc.to_string exn) ; + Lwt.return_none) | `Weeklynet _ -> (* There is no aliases for weeklynet. *) - Lwt.return_nil + Lwt.return_none | `Sandbox -> accounts |> List.map (fun account -> ( Some account.Account.alias, account.public_key_hash, account.public_key )) - |> Lwt.return + |> Lwt.return_some let aliases ?accounts network = let* aliases = delegates ?accounts network in - aliases |> List.to_seq - |> Seq.filter_map (function - | None, _, _ -> None - | Some alias, address, _ -> Some (address, alias)) - |> Hashtbl.of_seq |> Lwt.return + match aliases with + | None -> Lwt.return_none + | Some aliases -> + aliases |> List.to_seq + |> Seq.filter_map (function + | None, _, _ -> None + | Some alias, address, _ -> Some (address, alias)) + |> Hashtbl.of_seq |> Lwt.return_some diff --git a/tezt/tests/cloud/network.mli b/tezt/tests/cloud/network.mli index c9eeb906094e..03cf89645df6 100644 --- a/tezt/tests/cloud/network.mli +++ b/tezt/tests/cloud/network.mli @@ -43,20 +43,30 @@ val get_level : Endpoint.t -> int Lwt.t val expected_pow : t -> float (** Associate delegate aliases with octez version used (according to tzkt). - Only works for mainnet and ghosnet. Return empty table for other networks. *) -val versions : t -> (string, string) Hashtbl.t Lwt.t + Only works for mainnet and ghostnet. Return empty table for other networks. -(** List of delegates as [(alias, public key hash, public key)] known by tzkt - for a given network. - [?accounts] is only used for [`Sandbox] network - (and the only source of data for this kind of network) *) + Returns [None] when [Tzkt] did not respond or respond with an unexpected + output. +*) +val versions : t -> (string, string) Hashtbl.t option Lwt.t + +(** List of delegates as [(alias, public key hash, public key)] known + by tzkt for a given network. [?accounts] is only used for + [`Sandbox] network (and the only source of data for this kind of + network). + + Returns [None] when [Tzkt] did not respond or respond with an unexpected + output. *) val delegates : ?accounts:Account.key list -> t -> - (string option * string * string) list Lwt.t + (string option * string * string) list option Lwt.t (** Table of delegates as [(pkh,alias)] known by tzkt for a given network [?accounts] is only used for [`Sandbox] network - (and the only source of data for this kind of network) *) + (and the only source of data for this kind of network). + + Returns [None] when [Tzkt] did not respond or respond with a bad + output. *) val aliases : - ?accounts:Account.key list -> t -> (string, string) Hashtbl.t Lwt.t + ?accounts:Account.key list -> t -> (string, string) Hashtbl.t option Lwt.t -- GitLab