/* eIM testsuite in TTCN-3
 *
 * Author: Philipp Maier <pmaier@sysmocom.de> / sysmocom - s.f.m.c. GmbH
 *
 * Released under the terms of GNU General Public License, Version 2 or
 * (at your option) any later version.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

module eIM_Tests {

import from Misc_Helpers all;
import from General_Types all;
import from Osmocom_Types all;

import from SGP32Definitions all;
import from SGP32Definitions_Types all;
import from SGP32Definitions_Templates all;

import from RSPDefinitions all;
import from RSPDefinitions_Types all;
import from RSPDefinitions_Templates all;

import from PKIX1Explicit88 all;
import from PKIX1Explicit88_Templates all;
import from PKIX1Explicit88_Types all;

import from PKIX1Implicit88 all;
import from PKIX1Implicit88_Templates all;
import from PKIX1Implicit88_Types all;

import from HTTP_Server_Emulation all;
import from HTTPmsg_Types all;

import from HTTPmsg_PortType all;
import from HTTP_Adapter all;

import from es9p_Types_JSON all;
import from REST_Types_JSON all;

/* the PIPEasp port allows us to interact with restop.py via stdin/stdout */
import from PIPEasp_PortType all;
import from PIPEasp_Types all;

const octetstring eID := '89882119900000000000000000000005'O;
const octetstring iccid := '123456789ABCDEFFAAAA'O;

type component MTC_CT {
	timer g_Tguard;

	/* HTTP server */
	var HTTP_Server_Emulation_CT vc_HTTP;
};

type component eIM_ConnHdlr extends HTTP_ConnHdlr, http_CT {
	port PIPEasp_PT PIPE;
	var eIM_ConnHdlrPars g_pars_EIM;
	var template integer g_http_client_id := omit; /* ESipa client */
};

type record eIM_ConnHdlrPars {
	/* add parameters here when necessary */
};

private function f_init_pars()
runs on MTC_CT return eIM_ConnHdlrPars {
	var eIM_ConnHdlrPars pars := {
		/* add parameters here when necessary */
	};
	return pars;
}

modulepar {
	/* emulated ES9+ SMDP+ server */
	charstring mp_es9p_ip := "127.0.0.1";
	integer mp_es9p_port := 8090;
	boolean mp_es9p_disable_ssl := false;

	/* emulated ESipa IPAd HTTPs client */
	charstring mp_esipa_ip := "127.0.0.1";
	integer mp_esipa_port := 8000;
	boolean mp_esipa_disable_ssl := false;

	/* REST API tool */
	charstring mp_restop_path;
}

private altstep as_Tguard() runs on MTC_CT {
	[] g_Tguard.timeout {
		Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Tguard timeout");
	}
}

private type function void_fn(charstring id) runs on eIM_ConnHdlr;

private function f_init_handler(void_fn fn, charstring id, eIM_ConnHdlrPars pars) runs on eIM_ConnHdlr {
	g_pars_EIM := pars;
	fn.apply(id);
}

private function f_start_handler(void_fn fn, eIM_ConnHdlrPars pars)
runs on MTC_CT return eIM_ConnHdlr {
	var eIM_ConnHdlr vc_conn;
	var charstring id := testcasename();

	vc_conn := eIM_ConnHdlr.create(id);

	/* connect ES9+/HTTP-server ports */
	if (isbound(vc_HTTP)) {
		connect(vc_conn:HTTP_SRV[0], vc_HTTP:CLIENT);
		connect(vc_conn:HTTP_SRV_PROC[0], vc_HTTP:CLIENT_PROC);
	}

	vc_conn.start(f_init_handler(fn, id, pars));
	return vc_conn;
}

private function f_init(charstring id, float t_guard := 40.0) runs on MTC_CT {
	g_Tguard.start(t_guard);
	activate(as_Tguard());

	/* start ES9+/HTTP-server */
	var HttpServerEmulationCfg http_cfg := {
		http_bind_ip := mp_es9p_ip,
		http_bind_port := mp_es9p_port,
		use_ssl := not mp_es9p_disable_ssl
	};
	vc_HTTP := HTTP_Server_Emulation_CT.create(id);
	vc_HTTP.start(HTTP_Server_Emulation.main(http_cfg));
}



/* ********************************************* */
/* ************* ES9+/HTTP-server ************** */
/* ********************************************* */

/* Helper function to send HTTP responses (ES9+) */
template (value) HTTPMessage ts_http_resp(charstring resp) := {
	response := {
		client_id := omit,
		version_major := 1,
		version_minor := 1,
		statuscode := 200,
		statustext := "OK",
		/* See also SGP.32, section 6.1.1 */
		header := {
			{
				header_name := "X-Admin-Protocol",
				header_value := "gsma/rsp/v1.0.0"
			},
			{
				header_name := "Content-Type",
				header_value := "application/json"
			},
			{
				header_name := "Content-Length",
				header_value := int2str(lengthof(resp))
			}
			},
		body := resp
	}
}


/* Receive one Es9p HTTP request */
private function f_es9p_receive() runs on eIM_ConnHdlr return RemoteProfileProvisioningRequest {
	var HTTPMessage es9p_req;
	timer T := 10.0;
	var RemoteProfileProvisioningRequest request;

	T.start;
	alt {
	[] HTTP_SRV[0].receive({ request := ? }) -> value es9p_req {
		dec_RemoteProfileProvisioningRequest_from_JSON(es9p_req.request.body, request);
		}
	[] T.timeout {
		setverdict(fail, "no HTTP request received?");
		}
	}

	return request;
}

/* Send Es9p HTTP response */
private function f_es9p_send(RemoteProfileProvisioningResponse es9p_res) runs on eIM_ConnHdlr  {
	var charstring es9p_res_json;
	enc_RemoteProfileProvisioningResponse_to_JSON(es9p_res, es9p_res_json);
	HTTP_SRV[0].send(ts_http_resp(es9p_res_json));
}

/* Perform Es9p HTTP request/response cycle */
private function f_es9p_transceive(RemoteProfileProvisioningResponse es9p_res,
				   template RemoteProfileProvisioningRequest expected_es9p_req := omit)
runs on eIM_ConnHdlr return RemoteProfileProvisioningRequest {
	var RemoteProfileProvisioningRequest es9p_req;
	es9p_req := f_es9p_receive();
	f_es9p_send(es9p_res);
	if (not istemplatekind(expected_es9p_req, "omit")) {
		if (not match(valueof(es9p_req), expected_es9p_req)) {
			setverdict(fail, "unexpected request from eIM on ES9p");
		}
	}
	return es9p_req;
}

/* Perform Es9p HTTP request/response cycle but with an empty response */
private function f_es9p_transceive_empty_response(template RemoteProfileProvisioningRequest expected_es9p_req := omit)
runs on eIM_ConnHdlr return RemoteProfileProvisioningRequest {
	var RemoteProfileProvisioningResponse es9p_res; /* Intentionally left unbound */
	return f_es9p_transceive(es9p_res, expected_es9p_req);
}



/* ********************************************* */
/* ************ PIPEasp (REST API) ************* */
/* ********************************************* */

template (value) ASP_PExecute ts_Exec(charstring cmd) := {
	command := cmd,
	stdin := ""
}

template (present) ASP_PResult tr_Result(template (present) charstring stdout,
					 template (present) charstring stderr,
					 template (present) integer code) := {
	stdout := stdout,
	stderr := stderr,
	code := code
}

private function f_init_pipe() runs on eIM_ConnHdlr {
	map(self:PIPE, system:PIPE);
}

/* Create a new rest resource (generic) */
private function f_rest_create(charstring cmdline)
runs on eIM_ConnHdlr return charstring {
	var ASP_PResult result;

	PIPE.send(ts_Exec(cmdline));
	timer T := 4.0;
	T.start;
	alt {
	[] PIPE.receive(tr_Result(?,?,0)) -> value result {
		/* On success, stdout should contain an URL to a resource */
		 if (substr(result.stdout, 0, 7) != "http://") {
			setverdict(fail, "got malformed resource URL from REST API while creating resource");
		 }
		return regexp(result.stdout, "(?+)(lookup/)(?+)", 2);
		}
	[] PIPE.receive	{
		setverdict(fail, "unexpected response from REST API while creating resource");
	}
	[] T.timeout {
		setverdict(fail, "no response from REST API while creating resource");
		}
	}

	setverdict(fail, "got no resource URL from REST API while creating resource");
	return "no resource";
}

/* Create a new rest resource (facility/order) */
private function f_rest_create_order(charstring facility, template JSON_REST_Resource order)
runs on eIM_ConnHdlr return charstring {
	var charstring cmdline;
	cmdline := mp_restop_path & " -c -f " & facility & " -j " &
		   oct2char(enc_JSON_REST_Resource(valueof(order)));
	return f_rest_create(cmdline);
}

/* Lookup an existing REST resource */
private function f_rest_lookup_resource(charstring resource_id, charstring facility,
					template JSON_REST_Response expected_rest_res := omit)
runs on eIM_ConnHdlr return JSON_REST_Response {
	var charstring cmdline;
	var ASP_PResult result;
	var JSON_REST_Response rest_res;

	cmdline := mp_restop_path & " -l -f " & facility & " -r " & resource_id;

	PIPE.send(ts_Exec(cmdline));
	timer T := 4.0;
	T.start;
	alt {
	[] PIPE.receive(tr_Result(?,?,0)) -> value result {
		rest_res := dec_JSON_REST_Response(char2oct(result.stdout));
		}
	[] PIPE.receive	{
		setverdict(fail, "unexpected response from REST API while looking up resource");
	}
	[] T.timeout {
		setverdict(fail, "no response from REST API while looking up resource");
		}
	}

	if (not istemplatekind(expected_rest_res, "omit") and not match(rest_res, tr_JSON_REST_success)) {
		setverdict(fail, "unexpected REST lookup result from eIM");
	}

	return rest_res;
}

/* Delete REST resource */
private function f_rest_delete_resource(charstring resource_id, charstring facility)
runs on eIM_ConnHdlr {
	var charstring cmdline;
	var ASP_PResult result;
	var JSON_REST_Response rest_res;
	cmdline := mp_restop_path & " -d -f " & facility & " -r " & resource_id;

	PIPE.send(ts_Exec(cmdline));
	timer T := 4.0;
	T.start;
	alt {
	[] PIPE.receive(tr_Result(?,?,0)) -> value result {
	   	/* Verify that the resource has been deleted */
		rest_res := dec_JSON_REST_Response(char2oct(result.stdout));
		if (not match(rest_res, tr_JSON_REST_deleted)) {
			setverdict(fail, "unexpected REST delete result from eIM");
		}

		/* Make sure that the resource has really been deleted */
		if (not match(f_rest_lookup_resource(resource_id, facility), tr_JSON_REST_absent)) {
			setverdict(fail, "unexpected REST result from eIM while checking resource deletion");
		}

		}
	[] PIPE.receive	{
		setverdict(fail, "unexpected response from REST API while deleting resource");
	}
	[] T.timeout {
		setverdict(fail, "no response from REST API while deleting resource");
		}
	}
}



/* ********************************************* */
/* ************* ESipa/HTTP-client ************* */
/* ********************************************* */

private function f_enc_http_req(in RemoteProfileProvisioningResponse es9p_res, out HTTPMessage http_res) {
	var charstring json_body;
	var HTTPResponse resp_body;

	enc_RemoteProfileProvisioningResponse_to_JSON(es9p_res, json_body);
	resp_body := {
		client_id := omit,
		version_major := 1,
		version_minor := 1,
		statuscode := 200,
		statustext := "OK",
		/* See also SGP.32, section 6.1.1 */
		header := {
			{
				header_name := "X-Admin-Protocol",
				header_value := "gsma/rsp/v1.0.0"
			},
			{
				header_name := "Content-Type",
				header_value := "application/x-gsma-rsp-asn1"
			},
			{
				header_name := "Content-Length",
				header_value := int2str(lengthof(json_body))
			}
			},
			body := json_body
		};

	http_res := { response := resp_body };
} with { extension "prototype(fast)" }

template (value) HeaderLines ts_esipa_HTTP_Header := {
	 { header_name := "User-Agent", header_value := "TTCN3 eIM testsuite" },
	 { header_name := "X-Admin-Protocol", header_value := "gsma/rsp/v2.5.0" },
	 { header_name := "Content-Type", header_value := "application/x-gsma-rsp-asn1" }
};

/* Send ESipa HTTP request */
private function f_esipa_send(EsipaMessageFromIpaToEim esipa_req) runs on eIM_ConnHdlr {
	var octetstring esipa_req_enc;
	esipa_req_enc := enc_EsipaMessageFromIpaToEim(esipa_req);
	f_http_tx_request(url := "/gsma/rsp2/asn1",
			  method := "POST",
			  binary_body:= esipa_req_enc,
			  custom_hdr := valueof(ts_esipa_HTTP_Header),
			  client_id := g_http_client_id);
}

/* Receive ESipa HTTP response */
private function f_esipa_receive(template EsipaMessageFromEimToIpa expected_esipa_res := omit)
runs on eIM_ConnHdlr return template EsipaMessageFromEimToIpa {
	var template EsipaMessageFromEimToIpa esipa_res;
	var HTTPMessage response_http;
	response_http := f_http_rx_response(exp := ?,
					    client_id := g_http_client_id,
					    keep_connection := true);

	if (istemplatekind(g_http_client_id, "omit")) {
		/* Memorize client_id of the first request. The client_id will identify the HTTP connection
		 * in all consecutive requests. */
		g_http_client_id := f_http_client_id_from_http_response(response_http);
	} else if (valueof(g_http_client_id) != valueof(f_http_client_id_from_http_response(response_http))) {
		/* The client_id must not change. A sudden change of the client_id may indicate an interrupted
		 * HTTP connection. This must not happen unexpectetly. */
		setverdict(fail, "unexpected ESipa HTTP connection");
	}

	if (ispresent(response_http.response_binary)) {
		esipa_res := dec_EsipaMessageFromEimToIpa(response_http.response_binary.body);
		if (not istemplatekind(expected_esipa_res, "omit")) {
			if (not match(valueof(esipa_res), expected_esipa_res)) {
				setverdict(fail, "unexpected response from eIM on ESipa");
			}
		}
		return esipa_res;
	} else {
		setverdict(fail,
			   "unexpected ESipa response, the HTTP body did either contain no or a non binary response");
		return omit;
	}
}

/* Perform one ESipa HTTP request/response cycle */
private function f_esipa_transceive(EsipaMessageFromIpaToEim esipa_req,
				    template EsipaMessageFromEimToIpa expected_esipa_res := omit)
runs on eIM_ConnHdlr return template EsipaMessageFromEimToIpa {
	var template EsipaMessageFromEimToIpa esipa_res
	f_esipa_send(esipa_req);
	esipa_res := f_esipa_receive();
	if (not istemplatekind(expected_esipa_res, "omit")) {
		if (not match(valueof(esipa_res), expected_esipa_res)) {
			setverdict(fail, "unexpected response from eIM on ESipa");
		}
	}
	return esipa_res;
}



/* ********************************************* */
/* ********** BELOW ONLY TESTCASES! ************ */
/* ********************************************* */

/* Common Mutual Authentication Procedure, see also: GSMA SGP.22, section 3.0.1 */
private function f_proc_cmn_mtl_auth() runs on eIM_ConnHdlr {
	var charstring smdpAddress := mp_es9p_ip & ":" & int2str(mp_es9p_port);

	/* InitiateAuthentication cycle */
	f_esipa_send(valueof(ts_initiateAuthenticationRequestEsipa(smdpAddress := smdpAddress)));
	f_es9p_transceive(valueof(ts_initiateAuthenticationResponse), tr_initiateAuthenticationRequest);
	f_esipa_receive(tr_initiateAuthenticationResponseEsipa);

	/* AuthenticateClient cycle */
	f_esipa_send(valueof(ts_authenticateClientRequestEsipa));
	f_es9p_transceive(valueof(ts_authenticateClientResponseEs9), tr_authenticateClientRequest);
	f_esipa_receive(tr_authenticateClientResponseEsipa_dpe);
}

/* A testcase to try out an indirect profile download,
 * See also: GSMA SGP.32, section 3.2.3.2: Indirect Profile Download */
private function f_TC_proc_indirect_prfle_dwnld(charstring id) runs on eIM_ConnHdlr {
	var charstring resource_id;
	var HTTP_Adapter_Params http_adapter_pars;
	var charstring smdpp_add := mp_es9p_ip & ":" & int2str(mp_es9p_port)
	var charstring activationCode := "1$" & smdpp_add & "$MyMatchingId"

	f_http_register();

	f_init_pipe();
	http_adapter_pars := {
		http_host := mp_esipa_ip,
		http_port := mp_esipa_port,
		use_ssl := not mp_esipa_disable_ssl
	};
	f_http_init(http_adapter_pars);

	/* Create a new download at the eIM */
	resource_id := f_rest_create_order("download", ts_JSON_REST_download_order(eID, activationCode));

	/* Check the eIM for new eIM packages, we expect to get a profileDownloadTriggerRequest that contains the
	 * activationCode that we have created the download with (see above) */
	f_esipa_transceive(valueof(ts_getEimPackageRequest(eID)), tr_getEimPackageResponse_dnlTrigReq(activationCode));

	/* Perform common mutial authentication procedure */
	f_proc_cmn_mtl_auth();

	/* Request/download bound profile package */
	f_esipa_send(valueof(ts_getBoundProfilePackageRequestEsipa));
	f_es9p_transceive(valueof(ts_getBoundProfilePackageResponse), tr_getBoundProfilePackageRequest);
	f_esipa_receive(tr_getBoundProfilePackageResponseEsipa);

	/* Handle notification */
	f_esipa_send(valueof(ts_handleNotificationEsipa_prfleInstRslt(notificationAddress := smdpp_add)));
	f_es9p_transceive_empty_response(tr_handleNotification);

	f_rest_lookup_resource(resource_id, "download", tr_JSON_REST_success);
	f_rest_delete_resource(resource_id, "download");

	f_sleep(2.0);
	setverdict(pass);
}
testcase TC_proc_indirect_prfle_dwnld() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_indirect_prfle_dwnld), pars);
	vc_conn.done;
	setverdict(pass);
}

/* A testcase to try out an the Generic eUICC Package Download and Execution Procedure (PSMO),
 * See also: GSMA SGP.32, section 3.3.1: Generic eUICC Package Download and Execution */
private function f_TC_proc_euicc_pkg_dwnld_exec_psmo_or_eco(charstring facility, charstring id,
							    JSON_REST_Resource order,
							    template (present) EuiccPackage euiccPackage,
							    template (present) EuiccPackageResult euiccPackageResult)
runs on eIM_ConnHdlr {
	var charstring resource_id;
	var HTTP_Adapter_Params http_adapter_pars;
	var template EsipaMessageFromEimToIpa esipa_res;

	f_http_register();

	f_init_pipe();
	http_adapter_pars := {
		http_host := mp_esipa_ip,
		http_port := mp_esipa_port,
		use_ssl := not mp_esipa_disable_ssl
	};
	f_http_init(http_adapter_pars);

	/* Create a new download at the eIM */
	resource_id := f_rest_create_order(facility, order);

	/* Check the eIM for new eIM packages, we expect to get an euiccPackageRequest that contains the PSMO
	 * that we have created (see above) */
	esipa_res := f_esipa_transceive(valueof(ts_getEimPackageRequest(str2oct(order.eidValue))),
					tr_getEimPackageResponse_euiccPkgReq(euiccPackage));

	/* The eIM will generate a counterValue and a transactionId, both must be echoed in the
	 * response. */
	euiccPackageResult.euiccPackageResultSigned.euiccPackageResultDataSigned.counterValue :=
		esipa_res.getEimPackageResponse.euiccPackageRequest.euiccPackageSigned.counterValue;
	euiccPackageResult.euiccPackageResultSigned.euiccPackageResultDataSigned.transactionId :=
		esipa_res.getEimPackageResponse.euiccPackageRequest.euiccPackageSigned.transactionId;

	/* Respond with a plausible EimPackage result */
	f_esipa_transceive(valueof(ts_provideEimPackageResult_ePRAndNotif(euiccPackageResult)),
			   tr_provideEimPackageResultResponse_eimAck);

	f_rest_lookup_resource(resource_id, facility, tr_JSON_REST_success);
	f_rest_delete_resource(resource_id, facility);

	f_sleep(2.0);
	setverdict(pass);
}

/* (see also above) Test enable PSMO */
private function f_TC_proc_euicc_pkg_dwnld_exec_enable_psmo(charstring id) runs on eIM_ConnHdlr {
	var JSON_REST_Resource psmo_order := valueof(ts_JSON_REST_psmo_order_enable(eID, iccid, false));
	var template EuiccPackage euiccPackage := tr_euiccPackage_enablePsmo;
	f_TC_proc_euicc_pkg_dwnld_exec_psmo_or_eco("psmo", id, psmo_order, euiccPackage,
						   ts_euiccPackageResultSigned_enablePsmo);
}
testcase TC_proc_euicc_pkg_dwnld_exec_enable_psmo() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_euicc_pkg_dwnld_exec_enable_psmo), pars);
	vc_conn.done;
	setverdict(pass);
}

/* (see also above) Test disable PSMO */
private function f_TC_proc_euicc_pkg_dwnld_exec_disable_psmo(charstring id) runs on eIM_ConnHdlr {
	var JSON_REST_Resource psmo_order := valueof(ts_JSON_REST_psmo_order_disable(eID, iccid));
	var template EuiccPackage euiccPackage := tr_euiccPackage_disablePsmo;
	f_TC_proc_euicc_pkg_dwnld_exec_psmo_or_eco("psmo", id, psmo_order, euiccPackage,
						   ts_euiccPackageResultSigned_disablePsmo);
}
testcase TC_proc_euicc_pkg_dwnld_exec_disable_psmo() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_euicc_pkg_dwnld_exec_disable_psmo), pars);
	vc_conn.done;
	setverdict(pass);
}

/* (see also above) Test delete PSMO */
private function f_TC_proc_euicc_pkg_dwnld_exec_delete_psmo(charstring id) runs on eIM_ConnHdlr {
	var JSON_REST_Resource psmo_order := valueof(ts_JSON_REST_psmo_order_delete(eID, iccid));
	var template EuiccPackage euiccPackage := tr_euiccPackage_deletePsmo;
	f_TC_proc_euicc_pkg_dwnld_exec_psmo_or_eco("psmo", id, psmo_order, euiccPackage,
						   ts_euiccPackageResultSigned_deletePsmo);
}
testcase TC_proc_euicc_pkg_dwnld_exec_delete_psmo() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_euicc_pkg_dwnld_exec_delete_psmo), pars);
	vc_conn.done;
	setverdict(pass);
}

/* (see also above) Test disable and delete PSMO at the same time to make sure that eUICC packages with multiple PSMOs
 * are also accepted and processed correctly. */
private function f_TC_proc_euicc_pkg_dwnld_exec_disable_and_delete_psmo(charstring id) runs on eIM_ConnHdlr {
	var JSON_REST_Resource psmo_order := valueof(ts_JSON_REST_psmo_order_disable_and_delete(eID, iccid));
	var template EuiccPackage euiccPackage := tr_euiccPackage_disableAndDeletePsmo;
	f_TC_proc_euicc_pkg_dwnld_exec_psmo_or_eco("psmo", id, psmo_order, euiccPackage,
						   ts_euiccPackageResultSigned_disableAndDeletePsmo);
}
testcase TC_proc_euicc_pkg_dwnld_exec_disable_and_delete_psmo() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_euicc_pkg_dwnld_exec_disable_and_delete_psmo), pars);
	vc_conn.done;
	setverdict(pass);
}

/* (see also above) Test listProfileInfo PSMO */
private function f_TC_proc_euicc_pkg_dwnld_exec_listProfileInfo_psmo(charstring id) runs on eIM_ConnHdlr {
	var JSON_REST_Resource psmo_order := valueof(ts_JSON_REST_psmo_order_listProfileInfo(eID, iccid));
	var template EuiccPackage euiccPackage := tr_euiccPackage_listProfileInfo;
	f_TC_proc_euicc_pkg_dwnld_exec_psmo_or_eco("psmo", id, psmo_order, euiccPackage,
						   ts_euiccPackageResultSigned_listProfileInfo);
}
testcase TC_proc_euicc_pkg_dwnld_exec_listProfileInfo_psmo() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_euicc_pkg_dwnld_exec_listProfileInfo_psmo), pars);
	vc_conn.done;
	setverdict(pass);
}

/* (see also above) Test getRAT PSMO */
private function f_TC_proc_euicc_pkg_dwnld_exec_getRAT_psmo(charstring id) runs on eIM_ConnHdlr {
	var JSON_REST_Resource psmo_order := valueof(ts_JSON_REST_psmo_order_getRAT(eID));
	var template EuiccPackage euiccPackage := tr_euiccPackage_getRAT;
	f_TC_proc_euicc_pkg_dwnld_exec_psmo_or_eco("psmo", id, psmo_order, euiccPackage,
						   ts_euiccPackageResultSigned_getRAT);
}
testcase TC_proc_euicc_pkg_dwnld_exec_getRAT_psmo() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_euicc_pkg_dwnld_exec_getRAT_psmo), pars);
	vc_conn.done;
	setverdict(pass);
}

/* (see also above) Test configureAutoEnable PSMO */
private function f_TC_proc_euicc_pkg_dwnld_exec_configureAutoEnable_psmo(charstring id) runs on eIM_ConnHdlr {
	var JSON_REST_Resource psmo_order := valueof(ts_JSON_REST_psmo_order_configureAutoEnable(eID));
	var template EuiccPackage euiccPackage := tr_euiccPackage_configureAutoEnable;
	f_TC_proc_euicc_pkg_dwnld_exec_psmo_or_eco("psmo", id, psmo_order, euiccPackage,
						   ts_euiccPackageResultSigned_configureAutoEnable);
}
testcase TC_proc_euicc_pkg_dwnld_exec_configureAutoEnable_psmo() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_euicc_pkg_dwnld_exec_configureAutoEnable_psmo), pars);
	vc_conn.done;
	setverdict(pass);
}

/* (see also above) Test addEim eCO */
private function f_TC_proc_euicc_pkg_dwnld_exec_addEim_eco(charstring id) runs on eIM_ConnHdlr {
	var JSON_REST_Resource eco_order := valueof(ts_JSON_REST_eco_order_addEim(eID));
	var template EuiccPackage euiccPackage := tr_euiccPackage_addEim;
	f_TC_proc_euicc_pkg_dwnld_exec_psmo_or_eco("eco", id, eco_order, euiccPackage,
						   ts_euiccPackageResultSigned_addEim);
}
testcase TC_proc_euicc_pkg_dwnld_exec_addEim_eco() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_euicc_pkg_dwnld_exec_addEim_eco), pars);
	vc_conn.done;
	setverdict(pass);
}

/* (see also above) Test deleteEim eCO */
private function f_TC_proc_euicc_pkg_dwnld_exec_deleteEim_eco(charstring id) runs on eIM_ConnHdlr {
	var JSON_REST_Resource eco_order := valueof(ts_JSON_REST_eco_order_deleteEim(eID));
	var template EuiccPackage euiccPackage := tr_euiccPackage_deleteEim;
	f_TC_proc_euicc_pkg_dwnld_exec_psmo_or_eco("eco", id, eco_order, euiccPackage,
						   ts_euiccPackageResultSigned_deleteEim);
}
testcase TC_proc_euicc_pkg_dwnld_exec_deleteEim_eco() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_euicc_pkg_dwnld_exec_deleteEim_eco), pars);
	vc_conn.done;
	setverdict(pass);
}

/* (see also above) Test updateEim eCO */
private function f_TC_proc_euicc_pkg_dwnld_exec_updateEim_eco(charstring id) runs on eIM_ConnHdlr {
	var JSON_REST_Resource eco_order := valueof(ts_JSON_REST_eco_order_updateEim(eID));
	var template EuiccPackage euiccPackage := tr_euiccPackage_updateEim;
	f_TC_proc_euicc_pkg_dwnld_exec_psmo_or_eco("eco", id, eco_order, euiccPackage,
						   ts_euiccPackageResultSigned_updateEim);
}
testcase TC_proc_euicc_pkg_dwnld_exec_updateEim_eco() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_euicc_pkg_dwnld_exec_updateEim_eco), pars);
	vc_conn.done;
	setverdict(pass);
}

/* (see also above) Test listEim eCO */
private function f_TC_proc_euicc_pkg_dwnld_exec_listEim_eco(charstring id) runs on eIM_ConnHdlr {
	var JSON_REST_Resource eco_order := valueof(ts_JSON_REST_eco_order_listEim(eID));
	var template EuiccPackage euiccPackage := tr_euiccPackage_listEim;
	f_TC_proc_euicc_pkg_dwnld_exec_psmo_or_eco("eco", id, eco_order, euiccPackage,
						   ts_euiccPackageResultSigned_listEim);
}
testcase TC_proc_euicc_pkg_dwnld_exec_listEim_eco() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_euicc_pkg_dwnld_exec_updateEim_eco), pars);
	vc_conn.done;
	setverdict(pass);
}

/* A testcase to try out an eUICC data request,
 * See also: GSMA SGP.32, section 2.11.1.2: IpaEuiccDataRequest */
private function f_TC_proc_euicc_data_req(charstring id) runs on eIM_ConnHdlr {
	var charstring resource_id;
	var HTTP_Adapter_Params http_adapter_pars;

	f_http_register();

	f_init_pipe();
	http_adapter_pars := {
		http_host := mp_esipa_ip,
		http_port := mp_esipa_port,
		use_ssl := not mp_esipa_disable_ssl
	};
	f_http_init(http_adapter_pars);

	/* Create a new download at the eIM */
	resource_id := f_rest_create_order("edr", ts_JSON_REST_edr_order(eID, '80BF20BF228384A5A688A9BF2B'O));

	/* Check the eIM for new eIM packages, we expect to get a getEimPackageResponse that contains an
	 * ipaEuiccDataRequest */
	f_esipa_transceive(valueof(ts_getEimPackageRequest(eID)), tr_getEimPackageResponse_euiccDataReq);

	/* Provide a plausible ipaEuiccDataResponse to the eIM */
	f_esipa_transceive(valueof(ts_provideEimPackageResult_euiccDataResp),
			   tr_provideEimPackageResultResponse_eimAck);

	f_rest_lookup_resource(resource_id, "edr", tr_JSON_REST_success);
	f_rest_delete_resource(resource_id, "edr");
}
testcase TC_proc_euicc_data_req() runs on MTC_CT {
	var charstring id := testcasename();
	var eIM_ConnHdlrPars pars := f_init_pars();
	var eIM_ConnHdlr vc_conn;
	f_init(id);
	vc_conn := f_start_handler(refers(f_TC_proc_euicc_data_req), pars);
	vc_conn.done;
	setverdict(pass);
}

control {
	execute ( TC_proc_indirect_prfle_dwnld() );
	execute ( TC_proc_euicc_pkg_dwnld_exec_enable_psmo() );
	execute ( TC_proc_euicc_pkg_dwnld_exec_disable_psmo() );
	execute ( TC_proc_euicc_pkg_dwnld_exec_delete_psmo() );
	execute ( TC_proc_euicc_pkg_dwnld_exec_disable_and_delete_psmo() );
	execute ( TC_proc_euicc_pkg_dwnld_exec_listProfileInfo_psmo() );
	execute ( TC_proc_euicc_pkg_dwnld_exec_getRAT_psmo() );
	execute ( TC_proc_euicc_pkg_dwnld_exec_configureAutoEnable_psmo() );
	execute ( TC_proc_euicc_pkg_dwnld_exec_addEim_eco() );
	execute ( TC_proc_euicc_pkg_dwnld_exec_deleteEim_eco() );
	execute ( TC_proc_euicc_pkg_dwnld_exec_updateEim_eco() );
	execute ( TC_proc_euicc_pkg_dwnld_exec_listEim_eco() );
	execute ( TC_proc_euicc_data_req() );
}

}