forked from yvv/decision
v3 : modules d'ordre et de titre — moteur, données, serveur (L11)
L'ordre des articles de chaque section (cran 2) et le titre du document (cran 4) deviennent des modules : des unités à part, dont les versions sont des propositions votées comme les articles (dépliées, panier, chaîne). - moteur : ld-engine/src/order.rs (qui est dans l'ordre, ordre proposé, complété à la fin quand un article entre ou sort, places redistribuées, nouvel article en fin de section) ; pending : un ordre de moins de deux articles n'est jamais à voter ; - données : migration 0007 (units.kind, module_amendments) ; le seed déclare ses modules ; module_amend.rs les ajoute une fois, gardé et journalisé, aux documents nés sans eux, sans toucher articles ni votes ; - serveur : modules.rs, order.rs, doc_title.rs (portes pour proposer, entrée en vigueur : les articles changent de place, le document change de titre, en et es passent « à revoir ») ; publication des modules ; - reste mineur : la médiane compte les statuts open, in_force, applied, et eux seuls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -618,10 +618,10 @@ async fn a_session_sees_its_own_ballots_only() {
|
||||
assert_eq!((find(&mine, &i1)["myVote"].as_str(), find(&mine, &e1)["myVote"].as_str()), (Some("for"), None));
|
||||
let theirs = other.doc("monnaie-libre").await;
|
||||
assert_eq!((find(&theirs, &i1)["myVote"].as_str(), find(&theirs, &e1)["myVote"].as_str()), (None, Some("against")));
|
||||
// « Décisions » counts the same way.
|
||||
// « Décisions » counts the same way (33 articles, the title, 7 orders with something to decide).
|
||||
let d = chiara.ok("GET", "/api/groups/monnaie-libre/decisions", None).await;
|
||||
assert_eq!(d["canVote"], true);
|
||||
assert_eq!((d["docs"][0]["toVote"].as_u64(), d["docs"][0]["withoutMyVote"].as_u64()), (Some(33), Some(32)));
|
||||
assert_eq!((d["docs"][0]["toVote"].as_u64(), d["docs"][0]["withoutMyVote"].as_u64()), (Some(41), Some(40)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -668,6 +668,81 @@ async fn fifty_voices_on_the_chain_make_a_text_pass_and_a_signal_holds_it() {
|
||||
assert!(unit(&d, "E5")["inForce"].is_null(), "a signal holds it back");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fifty_voices_on_the_chain_bring_an_order_and_a_title_into_force() {
|
||||
// The modules (ADR 0009) are voted like articles: in a basket, in one transaction.
|
||||
let indexer = Indexer::start().await;
|
||||
let (mut c, state) = Client::new(chain_config(&indexer)).await;
|
||||
c.login("Chiara").await;
|
||||
let doc = c.doc("monnaie-libre").await;
|
||||
let order = doc["modules"]["orders"]
|
||||
.as_array()
|
||||
.expect("orders")
|
||||
.iter()
|
||||
.find(|o| o["section"] == "conclusion")
|
||||
.expect("conclusion")
|
||||
.clone();
|
||||
let (k1, k2) = (unit(&doc, "K1")["id"].as_str().expect("K1"), unit(&doc, "K2")["id"].as_str().expect("K2"));
|
||||
assert_eq!(order["members"], json!([k1, k2]));
|
||||
let o = c
|
||||
.ok(
|
||||
"POST",
|
||||
&format!("/api/units/{}/order", order["id"].as_str().expect("id")),
|
||||
Some(json!({ "order": [k2, k1] })),
|
||||
)
|
||||
.await["id"]
|
||||
.as_str()
|
||||
.expect("order")
|
||||
.to_owned();
|
||||
let title_unit = doc["modules"]["title"]["id"].as_str().expect("title").to_owned();
|
||||
let t = c
|
||||
.ok(
|
||||
"POST",
|
||||
&format!("/api/units/{title_unit}/title"),
|
||||
Some(json!({ "title": "Acte d'engagement de certification" })),
|
||||
)
|
||||
.await["id"]
|
||||
.as_str()
|
||||
.expect("title")
|
||||
.to_owned();
|
||||
let doc = c.doc("monnaie-libre").await;
|
||||
let keys: Vec<String> = (1..=50u8).map(|n| address::encode(G1_PREFIX, &[n; 32])).collect();
|
||||
let mut last = Value::Null;
|
||||
for (n, account) in keys.iter().enumerate() {
|
||||
let b = c.ballot(&doc, &[(&o, Some("for")), (&t, Some("for"))], None).await;
|
||||
let index = 20_000 + u32::try_from(n).expect("n");
|
||||
indexer.pay(
|
||||
2_000 + index,
|
||||
b["comment"].as_str().expect("c"),
|
||||
Signer { account, identity: Some((index, "Membre", true)) },
|
||||
);
|
||||
last = b;
|
||||
if n % 10 == 9 {
|
||||
ballots::sweep(&state).await.expect("sweep");
|
||||
}
|
||||
}
|
||||
ballots::sweep(&state).await.expect("sweep");
|
||||
let s = c.status(&last).await;
|
||||
assert_eq!(s["status"], "confirmed");
|
||||
let mut entered: Vec<(&str, &str)> = s["entered"]
|
||||
.as_array()
|
||||
.expect("entered")
|
||||
.iter()
|
||||
.map(|e| (e["code"].as_str().unwrap_or_default(), e["kind"].as_str().unwrap_or_default()))
|
||||
.collect();
|
||||
entered.sort_unstable();
|
||||
assert_eq!(entered, [("ordre.conclusion", "order"), ("titre", "title")], "{s}");
|
||||
let d = c.doc("monnaie-libre").await;
|
||||
let (p1, p2) = (unit(&d, "K1")["position"].as_f64().expect("p"), unit(&d, "K2")["position"].as_f64().expect("p"));
|
||||
assert!(p2 < p1, "K2 now reads before K1");
|
||||
assert_eq!(d["doc"]["title"]["fr"], "Acte d'engagement de certification");
|
||||
assert_eq!(d["modules"]["title"]["inForce"]["translations"]["en"]["stale"], true, "the English title, to review");
|
||||
// The adoptions keep what entered, by kind.
|
||||
let kinds: Vec<String> =
|
||||
sqlx::query_scalar("SELECT kind FROM adoptions ORDER BY kind").fetch_all(&state.db).await.expect("k");
|
||||
assert_eq!(kinds, ["order", "title"]);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Without the chain, and in managed groups
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user