v3 : sur une version retenue, « contre » et retrait oui, « pour » non (moteur, serveur, chaîne)
Décision d'Yvv du 01/11/2026 : « Retirer ou "contre" ». Le moteur dit la règle (objection::may_cast) ; le vote direct, le vote groupé et le bulletin de la chaîne l'appliquent par DocState::admits : « pour » 409 objection, « contre » et retrait acceptés, une ligne refusée refuse tout le vote. Un bulletin scellé avant l'objection compte tel qu'il est signé ; une version retenue n'entre jamais. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -776,6 +776,52 @@ async fn fifty_voices_on_the_chain_make_a_text_pass_and_an_objection_holds_it()
|
||||
assert!(unit(&d, "E5")["inForce"].is_null(), "an objection holds it back");
|
||||
}
|
||||
|
||||
/// Yvv, 2026-11-01: « Retirer ou "contre" ». On the chain too, a held version takes a
|
||||
/// ballot against it or a withdrawal, never a *for* — alone or among free lines.
|
||||
#[tokio::test]
|
||||
async fn on_the_chain_a_held_version_takes_against_and_withdrawal_never_for() {
|
||||
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 doc_id = doc["doc"]["id"].as_str().expect("id").to_owned();
|
||||
let (e1, e4, e5) = (version(&doc, "E1", "inForce"), version(&doc, "E4", "0"), version(&doc, "E5", "0"));
|
||||
let o = c
|
||||
.ok(
|
||||
"POST",
|
||||
&format!("/api/proposals/{e5}/objections"),
|
||||
Some(json!({ "otherId": e1, "kind": "contredit", "reason": "Ces deux textes disent le contraire." })),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(o["heldId"], e5.as_str());
|
||||
let uri = format!("/api/docs/{doc_id}/ballots");
|
||||
let refused = (StatusCode::CONFLICT, Some("objection"));
|
||||
for lines in [
|
||||
json!([{ "proposalId": e5, "choice": "for" }]),
|
||||
json!([{ "proposalId": e4, "choice": "for" }, { "proposalId": e5, "choice": "for" }]),
|
||||
] {
|
||||
let (s, v) = c.call("POST", &uri, Some(json!({ "lines": lines }))).await;
|
||||
assert_eq!((s, v["error"].as_str()), refused, "{lines}");
|
||||
}
|
||||
// Against the held one, for a free one: sealed, then confirmed by the chain.
|
||||
let b = c.ballot(&doc, &[(&e4, Some("for")), (&e5, Some("against"))], None).await;
|
||||
indexer.pay(200, comment_of(&b), alice());
|
||||
ballots::sweep(&state).await.expect("sweep");
|
||||
assert_eq!(c.status(&b).await["status"], "confirmed");
|
||||
let d = c.doc("monnaie-libre").await;
|
||||
assert_eq!((tally(&d, &e4), tally(&d, &e5)), ((1, 0), (0, 1)));
|
||||
assert_eq!(find(&d, &e5)["myVote"], "against");
|
||||
// A withdrawal: the last ballot stands.
|
||||
let w = c.ballot(&doc, &[(&e5, None)], None).await;
|
||||
indexer.pay(201, comment_of(&w), alice());
|
||||
ballots::sweep(&state).await.expect("sweep");
|
||||
assert_eq!(c.status(&w).await["status"], "confirmed");
|
||||
let d = c.doc("monnaie-libre").await;
|
||||
assert_eq!(tally(&d, &e5), (0, 0));
|
||||
assert!(find(&d, &e5)["myVote"].is_null());
|
||||
assert!(unit(&d, "E5")["inForce"].is_null(), "held, it never enters");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// After the independent review of 74f96be (lot B2): its probes, kept as tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user