store-only apps edit their listing on the store (storebound listing live / set: old -> new per field with the checks, a PATCH of only the edited fields; serve listing.set is the one store-only write, its token bound to the store ids; policy.rs STORE_ONLY_WRITES, write_with_confirm for store targets); screenshot captions per language and scene edited as a text edit of the shots copy JSON (JSON files keep their layout); Play content rating, target audience and App content answers in App info; docs/console-parity.md after the editors (73 rows: 55 complete, 18 with the reason) and tests/console-parity.test.ts keeping it honest

This commit is contained in:
CRTkafa 2026-10-14 00:53:38 +08:00
parent 3cea2af4e1
commit 7ac028421a
33 changed files with 852 additions and 157 deletions

View file

@ -18,6 +18,27 @@ fn owner_actions_are_dry_runs_from_the_window() {
assert_eq!(w["dryRun"], json!(true));
}
#[test]
fn store_only_apps_take_only_the_listing_text_write() {
use storebound_desktop::policy::{store_only_write, STORE_ONLY_WRITES};
assert_eq!(STORE_ONLY_WRITES, &["listing.set"]);
assert!(store_only_write("listing.set", Some(Kind::Write)));
// the folder writes, owner actions and reads are not store-only writes
for m in ["play.listing", "apple.listing", "project.edit", "play.images"] {
assert!(!store_only_write(m, Some(Kind::Write)), "{m}");
}
assert!(!store_only_write("listing.set", Some(Kind::Owner)));
assert!(!store_only_write("listing.set", Some(Kind::Read)));
// a dry run from the window, like every write
let p = sanitize_js_call("listing.set", Some(Kind::Write), &json!({ "args": { "store": "play", "lang": "en-US", "fields": "{\"short\":\"x\"}" } })).unwrap();
assert_eq!(p["dryRun"], json!(true));
// the token binds the store ids instead of a folder: another app's ids give another token
let secret = [9u8; 32];
let a = json!({ "virtual": { "name": "com.example.a", "play": { "packageName": "com.example.a" } }, "args": { "lang": "en-US" } });
let b = json!({ "virtual": { "name": "com.example.b", "play": { "packageName": "com.example.b" } }, "args": { "lang": "en-US" } });
assert_ne!(mint(&secret, "listing.set", &a, 1_000, "n1").unwrap(), mint(&secret, "listing.set", &b, 1_000, "n1").unwrap());
}
#[test]
fn owner_dialogs_name_the_action() {
let (title, ok) = dialog_labels(Kind::Owner, "Change the Play staged rollout");