v3 : serveur des objections, des coquilles validées et de la messagerie (lot F)
Objections « redit » / « contredit » à la place des signalements : la dernière arrivée des deux versions n'est plus votable (repli, panier, chaîne : un vote déjà scellé est gardé mais ne la fait pas entrer) ; un texte en vigueur ne l'est jamais. L'auteur répond en modifiant l'article visé (proposition liée) ou en reformulant ; la personne qui objecte lève ou réitère depuis ses messages ; sans réponse sous 3 jours, l'objection est levée. Un administrateur peut lever. Coquilles : la sienne passe tout de suite ; celle d'un autre attend l'auteur (valider / refuser), passe seule au bout de 3 jours, et l'auteur garde la main tant que le texte n'est pas en vigueur. En vigueur, seule une coquille validée corrige (auteur de la version, administrateur pour un texte semé). Deux coquilles en attente : la seconde se mesure au texte voté ; à l'application, celle qui ne colle plus est périmée. Messagerie dans l'application (boîte, non-lus, gestes, échéance), chaque personne ne lit que les siens. Échéances par le balayage existant, avec rattrapage au démarrage et horloge injectable (route de dev). Migration 0008 additive : les signalements deviennent des « contredit », sans perte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -625,17 +625,15 @@ async fn a_session_sees_its_own_ballots_only() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fifty_voices_on_the_chain_make_a_text_pass_and_a_signal_holds_it() {
|
||||
async fn fifty_voices_on_the_chain_make_a_text_pass_and_an_objection_holds_it() {
|
||||
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 e4 = version(&doc, "E4", "0");
|
||||
let e5 = version(&doc, "E5", "0");
|
||||
// A contradiction signalled on E5 against the text in force E1: E5 cannot enter.
|
||||
let reason = "Ces deux textes disent le contraire l'un de l'autre.";
|
||||
let e1 = version(&doc, "E1", "inForce");
|
||||
c.ok("POST", &format!("/api/proposals/{e5}/signals"), Some(json!({ "targetId": e1, "reason": reason }))).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() {
|
||||
@@ -646,6 +644,27 @@ async fn fifty_voices_on_the_chain_make_a_text_pass_and_a_signal_holds_it() {
|
||||
b["comment"].as_str().expect("c"),
|
||||
Signer { account, identity: Some((index, "Membre", true)) },
|
||||
);
|
||||
if n == 49 {
|
||||
// The last ballot is signed; before the chain confirms it, an objection holds
|
||||
// E5 back against the text in force E1 (ADR 0010): no new ballot may hold it.
|
||||
let o = c
|
||||
.ok(
|
||||
"POST",
|
||||
&format!("/api/proposals/{e5}/objections"),
|
||||
Some(json!({ "otherId": e1, "kind": "contredit", "reason": reason })),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(o["heldId"], e5.as_str());
|
||||
let id = doc["doc"]["id"].as_str().expect("id");
|
||||
let (s, v) = c
|
||||
.call(
|
||||
"POST",
|
||||
&format!("/api/docs/{id}/ballots"),
|
||||
Some(json!({ "lines": [{ "proposalId": e5, "choice": "for" }] })),
|
||||
)
|
||||
.await;
|
||||
assert_eq!((s, v["error"].as_str()), (StatusCode::CONFLICT, Some("objection")));
|
||||
}
|
||||
last = b;
|
||||
if n % 10 == 9 {
|
||||
// A session holds at most 20 pending ballots: the background task confirms on the way.
|
||||
@@ -664,8 +683,11 @@ async fn fifty_voices_on_the_chain_make_a_text_pass_and_a_signal_holds_it() {
|
||||
assert_eq!(s["entered"][0]["code"], "E4", "{s}");
|
||||
let d = c.doc("monnaie-libre").await;
|
||||
assert_eq!(unit(&d, "E4")["inForce"]["id"], e4.as_str());
|
||||
// A ballot sealed before the objection counts, as a vote already cast: kept, but
|
||||
// the version held back cannot enter into force.
|
||||
assert_eq!(tally(&d, &e5), (50, 0));
|
||||
assert!(unit(&d, "E5")["inForce"].is_null(), "a signal holds it back");
|
||||
assert_eq!(find(&d, &e5)["votable"], false);
|
||||
assert!(unit(&d, "E5")["inForce"].is_null(), "an objection holds it back");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user