fix: separate playground values from object descriptions
This commit is contained in:
@@ -28,6 +28,22 @@ fn collection(s: &mut BrowserSession, operation: &str, name: &str) -> Result<Jso
|
||||
&json!({"name":{"kind":"string","value":name}}).to_string(),
|
||||
)
|
||||
}
|
||||
fn assert_value(s: &mut BrowserSession, name: &str, value: i64) {
|
||||
let calls = s.calls;
|
||||
let observes = s.observes;
|
||||
let interfaces = s.interfaces;
|
||||
let result = s
|
||||
.manual_call(&format!("/playground/{name}"), "/", "counter", "get", "{}")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
result["result"],
|
||||
json!({"kind":"integer","value":value.to_string()})
|
||||
);
|
||||
assert_eq!(s.calls, calls + 1, "get dispatches exactly once");
|
||||
assert_eq!(s.observes, observes, "get does not implicitly Observe");
|
||||
assert_eq!(s.interfaces, interfaces, "get does not implicitly fetch");
|
||||
}
|
||||
|
||||
fn names(observed: &Json) -> Vec<&str> {
|
||||
observed["observation"]["children"]
|
||||
.as_array()
|
||||
@@ -38,14 +54,14 @@ fn names(observed: &Json) -> Vec<&str> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_observe_refreshes_counter_and_parent_but_does_not_retry_or_fill_interfaces() {
|
||||
fn explicit_observe_preserves_counter_metadata_and_does_not_call_retry_or_fill_interfaces() {
|
||||
let mut s = session();
|
||||
s.manual_observe("/book", 1.0).unwrap();
|
||||
let initial = s.manual_observe("/playground", 1.0).unwrap();
|
||||
assert_eq!(names(&initial), ["apple", "orange"]);
|
||||
assert_eq!(
|
||||
initial["observation"]["children"][0]["object"]["description"],
|
||||
"3"
|
||||
"An in-memory counter."
|
||||
);
|
||||
assert!(
|
||||
increment(&mut s, 4).is_err(),
|
||||
@@ -53,6 +69,7 @@ fn explicit_observe_refreshes_counter_and_parent_but_does_not_retry_or_fill_inte
|
||||
);
|
||||
assert_eq!(s.calls, 0);
|
||||
s.manual_interface("/", "counter").unwrap();
|
||||
assert_value(&mut s, "apple", 3);
|
||||
let stats = s.manual_stats();
|
||||
let result = increment(&mut s, 4).unwrap();
|
||||
assert_eq!(result["result"], json!({"kind":"integer","value":"7"}));
|
||||
@@ -60,7 +77,8 @@ fn explicit_observe_refreshes_counter_and_parent_but_does_not_retry_or_fill_inte
|
||||
assert!(result.get("textView").is_none());
|
||||
assert_eq!(s.observes, stats["observes"].as_u64().unwrap() as u32);
|
||||
assert_eq!(s.interfaces, stats["interfaces"].as_u64().unwrap() as u32);
|
||||
// A result did not silently fabricate a refreshed Client Object.
|
||||
assert_value(&mut s, "apple", 7);
|
||||
// Counter metadata is descriptive, not a cached counter value.
|
||||
assert_eq!(
|
||||
s.client
|
||||
.object(&s.session, "/playground/apple")
|
||||
@@ -69,9 +87,12 @@ fn explicit_observe_refreshes_counter_and_parent_but_does_not_retry_or_fill_inte
|
||||
.unwrap()
|
||||
.description
|
||||
.as_deref(),
|
||||
Some("3")
|
||||
Some("An in-memory counter.")
|
||||
);
|
||||
let calls = s.calls;
|
||||
let parent = s.manual_observe("/playground", 1.0).unwrap();
|
||||
assert_eq!(s.calls, calls, "Observe must not implicitly call get");
|
||||
assert_eq!(parent["observation"], initial["observation"]);
|
||||
assert_eq!(parent["cached"], false);
|
||||
assert_eq!(
|
||||
parent["observation"]["children"][0]["object"]["name"],
|
||||
@@ -79,18 +100,22 @@ fn explicit_observe_refreshes_counter_and_parent_but_does_not_retry_or_fill_inte
|
||||
);
|
||||
assert_eq!(
|
||||
parent["observation"]["children"][0]["object"]["description"],
|
||||
"7"
|
||||
"An in-memory counter."
|
||||
);
|
||||
let selected = s.manual_observe("/playground/apple", 1.0).unwrap();
|
||||
assert_eq!(selected["cached"], false);
|
||||
assert_eq!(selected["observation"]["object"]["description"], "7");
|
||||
assert_eq!(
|
||||
selected["observation"]["object"],
|
||||
initial["observation"]["children"][0]["object"]
|
||||
);
|
||||
assert_eq!(s.calls, calls, "Observe must not implicitly call get");
|
||||
assert_eq!(
|
||||
selected["textView"],
|
||||
wip_text_view::render_object(&s.fresh_object("/playground/apple").unwrap()).unwrap()
|
||||
);
|
||||
assert_eq!(s.manual_observe("/book", 1.0).unwrap()["cached"], true);
|
||||
assert_eq!(s.manual_interface("/", "counter").unwrap()["cached"], true);
|
||||
assert_eq!(s.calls, 1);
|
||||
assert_eq!(s.calls, 3, "two explicit gets and one increment");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -122,19 +147,36 @@ fn collection_refresh_removes_stale_children_and_recovers_from_retained_not_foun
|
||||
collection(&mut s, "create", "apple").unwrap();
|
||||
// Explicit fresh refresh recovers even from a retained Client NotFound.
|
||||
let selected = s.manual_observe("/playground/apple", 1.0).unwrap();
|
||||
assert_eq!(selected["observation"]["object"]["description"], "0");
|
||||
assert_eq!(
|
||||
selected["observation"]["object"]["description"],
|
||||
"An in-memory counter."
|
||||
);
|
||||
assert_value(&mut s, "apple", 0);
|
||||
assert_eq!(
|
||||
names(&s.manual_observe("/playground", 1.0).unwrap()),
|
||||
["apple", "orange", "pear"]
|
||||
);
|
||||
assert_eq!(increment(&mut s, 2).unwrap()["result"]["value"], "2");
|
||||
s.manual_observe("/playground", 1.0).unwrap();
|
||||
s.manual_call("/playground/apple", "/", "counter", "reset", "{}")
|
||||
.unwrap();
|
||||
assert_value(&mut s, "apple", 2);
|
||||
let calls = s.calls;
|
||||
let incremented = s.manual_observe("/playground/apple", 1.0).unwrap();
|
||||
assert_eq!(incremented["observation"], selected["observation"]);
|
||||
let parent = s.manual_observe("/playground", 1.0).unwrap();
|
||||
assert_eq!(s.calls, calls, "Observe must not implicitly call get");
|
||||
assert_eq!(
|
||||
s.manual_observe("/playground", 1.0).unwrap()["observation"]["children"][0]["object"]["description"],
|
||||
"0"
|
||||
s.manual_call("/playground/apple", "/", "counter", "reset", "{}")
|
||||
.unwrap()["result"],
|
||||
json!({"kind":"integer","value":"0"})
|
||||
);
|
||||
assert_value(&mut s, "apple", 0);
|
||||
assert_eq!(s.calls, calls + 2, "one reset and one explicit get");
|
||||
let reset = s.manual_observe("/playground/apple", 1.0).unwrap();
|
||||
assert_eq!(reset["observation"], selected["observation"]);
|
||||
assert_eq!(
|
||||
s.manual_observe("/playground", 1.0).unwrap()["observation"],
|
||||
parent["observation"]
|
||||
);
|
||||
assert_eq!(s.calls, calls + 2, "Observe must not implicitly call get");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -143,9 +185,14 @@ fn failures_are_atomic_and_each_explicit_call_dispatches_at_most_once() {
|
||||
s.manual_observe("/playground", 2.0).unwrap();
|
||||
s.manual_interface("/", "counter").unwrap();
|
||||
s.manual_interface("/", "collection").unwrap();
|
||||
increment(&mut s, wip_protocol::MAX_SAFE_INTEGER - 3).unwrap();
|
||||
let maximum = s.manual_observe("/playground/apple", 1.0).unwrap();
|
||||
assert_eq!(
|
||||
increment(&mut s, wip_protocol::MAX_SAFE_INTEGER - 3).unwrap()["result"],
|
||||
json!({"kind":"integer","value":wip_protocol::MAX_SAFE_INTEGER.to_string()})
|
||||
);
|
||||
assert_value(&mut s, "apple", wip_protocol::MAX_SAFE_INTEGER);
|
||||
let calls = s.calls;
|
||||
let maximum = s.manual_observe("/playground/apple", 1.0).unwrap();
|
||||
assert_eq!(s.calls, calls, "Observe must not implicitly call get");
|
||||
let failure = increment(&mut s, 1).unwrap_err();
|
||||
assert_eq!(failure.kind, "protocol");
|
||||
assert_eq!(failure.code.as_deref(), Some("Internal"));
|
||||
@@ -154,15 +201,31 @@ fn failures_are_atomic_and_each_explicit_call_dispatches_at_most_once() {
|
||||
s.manual_observe("/playground/apple", 1.0).unwrap()["observation"],
|
||||
maximum["observation"]
|
||||
);
|
||||
assert_eq!(s.calls, calls + 1, "Observe must not implicitly call get");
|
||||
assert_value(&mut s, "apple", wip_protocol::MAX_SAFE_INTEGER);
|
||||
assert_eq!(
|
||||
s.calls,
|
||||
calls + 2,
|
||||
"one failed increment and one explicit get"
|
||||
);
|
||||
for name in ["apple", "bad/name", "<img>", ""] {
|
||||
let before = s.manual_observe("/playground", 1.0).unwrap();
|
||||
let calls = s.calls;
|
||||
let before = s.manual_observe("/playground", 1.0).unwrap();
|
||||
assert_eq!(s.calls, calls, "Observe must not implicitly call get");
|
||||
assert!(collection(&mut s, "create", name).is_err());
|
||||
assert_eq!(s.calls, calls + 1);
|
||||
assert_eq!(
|
||||
s.manual_observe("/playground", 1.0).unwrap()["observation"],
|
||||
before["observation"]
|
||||
);
|
||||
assert_eq!(s.calls, calls + 1, "Observe must not implicitly call get");
|
||||
assert_value(&mut s, "apple", wip_protocol::MAX_SAFE_INTEGER);
|
||||
assert_value(&mut s, "orange", 0);
|
||||
assert_eq!(
|
||||
s.calls,
|
||||
calls + 3,
|
||||
"one failed create and two explicit gets"
|
||||
);
|
||||
}
|
||||
let calls = s.calls;
|
||||
assert!(increment(&mut s, i64::MAX).is_err());
|
||||
@@ -170,6 +233,8 @@ fn failures_are_atomic_and_each_explicit_call_dispatches_at_most_once() {
|
||||
s.calls, calls,
|
||||
"out-of-range typed arguments rejected before Host dispatch"
|
||||
);
|
||||
assert_value(&mut s, "apple", wip_protocol::MAX_SAFE_INTEGER);
|
||||
assert_eq!(s.calls, calls + 1, "only the explicit get dispatches");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -189,29 +254,39 @@ fn normal_tree_and_document_reads_ignore_dynamic_children_and_reload_resets_stat
|
||||
let book = s.read("/book").unwrap();
|
||||
s.manual_interface("/", "counter").unwrap();
|
||||
s.manual_interface("/", "collection").unwrap();
|
||||
increment(&mut s, 10).unwrap();
|
||||
assert_eq!(increment(&mut s, 10).unwrap()["result"]["value"], "13");
|
||||
assert_value(&mut s, "apple", 13);
|
||||
collection(&mut s, "create", "pear").unwrap();
|
||||
collection(&mut s, "remove", "orange").unwrap();
|
||||
s.manual_observe("/playground", 1.0).unwrap();
|
||||
assert_eq!(s.tree().unwrap(), tree);
|
||||
assert_eq!(s.read("/playground").unwrap(), document);
|
||||
assert_eq!(s.read("/book").unwrap(), book);
|
||||
let calls = s.calls;
|
||||
assert_eq!(
|
||||
s.manual_observe("/playground/apple", 1.0).unwrap()["observation"]["object"]["description"],
|
||||
"13",
|
||||
"navigation does not reset state"
|
||||
"An in-memory counter."
|
||||
);
|
||||
assert_eq!(s.calls, calls, "Observe must not implicitly call get");
|
||||
// Navigation does not reset state; verify through an explicit operation.
|
||||
assert_value(&mut s, "apple", 13);
|
||||
let mut reloaded = session();
|
||||
let initial = reloaded.manual_observe("/playground", 1.0).unwrap();
|
||||
assert_eq!(names(&initial), ["apple", "orange"]);
|
||||
assert_eq!(
|
||||
initial["observation"]["children"][0]["object"]["description"],
|
||||
"3"
|
||||
"An in-memory counter."
|
||||
);
|
||||
assert_eq!(
|
||||
initial["observation"]["children"][1]["object"]["description"],
|
||||
"0"
|
||||
"An in-memory counter."
|
||||
);
|
||||
assert_eq!(reloaded.calls, 0, "Observe must not implicitly call get");
|
||||
reloaded.manual_interface("/", "counter").unwrap();
|
||||
assert_value(&mut reloaded, "apple", 3);
|
||||
assert_value(&mut reloaded, "orange", 0);
|
||||
assert_eq!(reloaded.calls, 2, "two explicit gets after reload");
|
||||
assert_value(&mut s, "apple", 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user