#![allow(
clippy::expect_used,
clippy::unwrap_used,
clippy::panic,
clippy::uninlined_format_args,
clippy::collapsible_if,
clippy::redundant_clone,
clippy::needless_raw_string_hashes,
clippy::single_match,
clippy::redundant_closure_for_method_calls,
clippy::redundant_pattern_matching,
clippy::ignored_unit_patterns,
clippy::clone_on_copy,
clippy::manual_assert,
clippy::unwrap_in_result,
clippy::useless_vec
)]
use meerkat_mobkit::{
AuthPolicy, AuthProvider, BigQueryNaming, ConsolePolicy, ConsoleRestJsonRequest,
RuntimeDecisionInputs, RuntimeOpsPolicy, TrustedOidcRuntimeConfig,
build_runtime_decision_state, handle_console_rest_json_route,
};
use serde_json::{Value, json};
fn release_json() -> String {
include_str!("../assets/release-targets.json").to_string()
}
fn trusted_oidc() -> TrustedOidcRuntimeConfig {
TrustedOidcRuntimeConfig {
discovery_json:
r#"{"issuer":"https://trusted.mobkit.local","jwks_uri":"https://trusted.mobkit.local/.well-known/jwks.json"}"#
.to_string(),
jwks_json: r#"{"keys":[{"kid":"kid-current","kty":"oct","alg":"HS256","k":"cGhhc2U4LXRydXN0ZWQtY3VycmVudC1zZWNyZXQ"}]}"#
.to_string(),
audience: "meerkat-console".to_string(),
}
}
fn trusted_toml() -> String {
r#"
[[modules]]
id = "router"
command = "router-bin"
args = ["--mode", "fast"]
restart_policy = "always"
[[modules]]
id = "delivery"
command = "delivery-bin"
args = ["--sink", "test"]
restart_policy = "on_failure"
"#
.to_string()
}
fn decision_state() -> meerkat_mobkit::RuntimeDecisionState {
build_runtime_decision_state(RuntimeDecisionInputs {
bigquery: BigQueryNaming {
dataset: "phase0_contract_dataset".to_string(),
table: "phase0_contract_table".to_string(),
},
trusted_mobkit_toml: trusted_toml(),
auth: AuthPolicy {
default_provider: AuthProvider::GoogleOAuth,
email_allowlist: vec!["alice@example.com".to_string()],
},
trusted_oidc: trusted_oidc(),
console: ConsolePolicy {
require_app_auth: false,
..ConsolePolicy::default()
},
ops: RuntimeOpsPolicy::default(),
release_metadata_json: release_json(),
})
.expect("decision state builds")
}
#[test]
fn phase0_contract_004_console_rest_sse_contract_version_is_pinned_and_enforced() {
let artifact: Value = serde_json::from_str(include_str!(
"../../docs/rct/console-rest-sse-contract-v0.4.0.json"
))
.expect("contract artifact json should parse");
assert_eq!(artifact["contract_version"], json!("0.4.0"));
assert_eq!(artifact["version_pin"], json!("v0.4.0"));
let state = decision_state();
let experience_method = artifact["surfaces"]["rest"]["experience"]["method"]
.as_str()
.expect("experience method must be present");
let experience_path = artifact["surfaces"]["rest"]["experience"]["path"]
.as_str()
.expect("experience path must be present");
let experience_response = handle_console_rest_json_route(
&state,
&ConsoleRestJsonRequest {
method: experience_method.to_string(),
path: experience_path.to_string(),
auth: None,
},
);
assert_eq!(experience_response.status, 200);
assert_eq!(
experience_response.body["contract_version"],
artifact["contract_version"]
);
assert_eq!(
experience_response.body["base_panel"]["route"],
json!(experience_path)
);
for field in artifact["surfaces"]["rest"]["experience"]["response"]["required_top_level_fields"]
.as_array()
.expect("experience required field list must be present")
{
let field_name = field
.as_str()
.expect("experience required field must be string");
assert!(
experience_response.body.get(field_name).is_some(),
"experience response missing required field: {field_name}"
);
}
let modules_method = artifact["surfaces"]["rest"]["modules"]["method"]
.as_str()
.expect("modules method must be present");
let modules_path = artifact["surfaces"]["rest"]["modules"]["path"]
.as_str()
.expect("modules path must be present");
let modules_response = handle_console_rest_json_route(
&state,
&ConsoleRestJsonRequest {
method: modules_method.to_string(),
path: modules_path.to_string(),
auth: None,
},
);
assert_eq!(modules_response.status, 200);
for field in artifact["surfaces"]["rest"]["modules"]["response"]["required_top_level_fields"]
.as_array()
.expect("modules required field list must be present")
{
let field_name = field
.as_str()
.expect("modules required field must be string");
assert!(
modules_response.body.get(field_name).is_some(),
"modules response missing required field: {field_name}"
);
}
assert!(modules_response.body["modules"].is_array());
let send_error_codes = artifact["surfaces"]["rpc"]["methods"]["mobkit/console/send"]["errors"]
[0]["codes"]
.as_array()
.expect("send error codes must be present");
assert!(
!send_error_codes.contains(&json!(
meerkat_mobkit::CONSOLE_TIMELINE_REPLAY_UNAVAILABLE_CODE
)),
"send must not advertise timeline replay errors"
);
let query_timeline_error_codes = artifact["surfaces"]["rpc"]["methods"]
["mobkit/console/query_timeline"]["errors"][0]["codes"]
.as_array()
.expect("query_timeline error codes must be present");
assert!(
query_timeline_error_codes.contains(&json!(
meerkat_mobkit::CONSOLE_TIMELINE_REPLAY_UNAVAILABLE_CODE
)),
"query_timeline must advertise timeline replay errors"
);
let timeline_path = artifact["surfaces"]["sse"]["timeline"]["path"]
.as_str()
.expect("timeline path must be present");
let keep_alive_event = artifact["surfaces"]["sse"]["activity_feed_keep_alive_event"]
.as_str()
.expect("keep-alive event must be present");
assert_eq!(
experience_response.body["chat_inspector"]["observe_route"],
json!(timeline_path)
);
assert_eq!(
experience_response.body["activity_feed"]["source_route"],
json!(timeline_path)
);
assert_eq!(
experience_response.body["activity_feed"]["keep_alive"]["event"],
json!(keep_alive_event)
);
}