package main import ( "context" "strings" "testing" ) // novox/hq issue 494: a record may change a clause the knowledge base holds only as drafted, and a record whose // clauses another record links by number is never drafted again, so no link ever points at another clause. // draftedNotConfirmed drafts 0240 and has the second judgement reject its first clause: it stays drafted. func draftedNotConfirmed(t *testing.T, w *world) Key { t.Helper() k := Key{KindDecision, 240} draftUntil(t, w, k, DraftDrafted) e := mustGet(t, w.k.Store, k) r := e.Current.Clone() r.Clauses[0].Text += " NOT EXACT" if err := w.k.Store.Save(context.Background(), Version{By: "test", Why: "a draft the checker will reject", Record: r}, true, false, e.Revision); err != nil { t.Fatal(err) } r = draftUntil(t, w, k, DraftDone) if r.Clauses[0].Source != SourceDrafted || !r.Accepted() { t.Fatalf("0240 §1 is not a drafted clause of an accepted record: %s %+v", r.Status, r.Clauses[0]) } return k } func TestARecordMayChangeAClauseHeldOnlyAsDrafted(t *testing.T) { eachStore(t, func(t *testing.T, s Store) { w := imported(t, s) k := draftedNotConfirmed(t, w) n := w.reserved(t, "author@test") p := valid(n) p.Links = append(p.Links, LinkAsk{Kind: LinkChanges, To: "0240 §1", From: 2}) if _, err := w.k.Propose("author@test", p); err != nil { t.Fatalf("a change of a drafted clause was refused: %v", err) } w.reviewAll(t) if e := mustGet(t, s, Key{KindDecision, n}); !e.Current.Accepted() { t.Fatalf("not accepted: %s", e.Current.Status) } // The drafted clause is changed by it, keeps its number and stays drafted: still never served as in force. r := mustGet(t, s, k).Current c := r.Clause(1) want := itoa4(n) + " §2" if c == nil || c.Status != ClauseChanged || c.Source != SourceDrafted || len(c.ChangedBy) != 1 || !strings.HasSuffix(c.ChangedBy[0], want) { t.Fatalf("0240 §1 after the change: %+v", c) } if Served(r, *c) { t.Fatal("a drafted clause that was changed is served as holding") } g, err := w.k.Get(GetAsk{Kind: KindDecision, Number: 240}) if err != nil { t.Fatal(err) } if h := g.Clauses[0].Holds; !strings.HasPrefix(h, "drafted, not confirmed") || !strings.Contains(h, "changed by ADR "+want) { t.Fatalf("get says of 0240 §1: %q", h) } // The whole-set rules find nothing wrong with the change. f, _ := s.Findings(context.Background()) for _, x := range f { if x.Rule == "partial" { t.Fatalf("a whole-set finding: %+v", x) } } }) } func TestARecordWhoseClausesAreLinkedByNumberIsNeverReturned(t *testing.T) { eachStore(t, func(t *testing.T, s Store) { w := imported(t, s) k := draftedNotConfirmed(t, w) // A proposed record cites 0240 §2: returning 0240 would point it at whatever is drafted second next time. n := w.reserved(t, "author@test") p := valid(n) p.Links = append(p.Links, LinkAsk{Kind: LinkCites, To: "0240 §2", From: 1}) w.judge.review = `{"verdict":"changes","findings":["not yet"]}` if _, err := w.k.Propose("author@test", p); err != nil { t.Fatal(err) } w.reviewAll(t) before := mustGet(t, s, k) _, err := w.k.Return("operator@shanks", k, "clause 1 says more than the section") if err == nil { t.Fatal("a record a proposed record links by clause was returned") } if !strings.Contains(err.Error(), "ADR "+itoa4(n)+" (proposed) cites §2") || !strings.Contains(err.Error(), "changes 0240 §N") { t.Fatalf("the refusal does not say what holds the clauses or the way forward: %v", err) } after := mustGet(t, s, k) if after.Versions != before.Versions || len(after.Current.Clauses) != len(before.Current.Clauses) || len(w.said.of(EventReturned)) != 0 { t.Fatalf("a refused return changed 0240: %d versions (was %d), %d clauses", after.Versions, before.Versions, len(after.Current.Clauses)) } // A clause another record changed holds the record to its numbers too, with no link left to scan. w2 := imported(t, newMemStore()) k2 := draftedNotConfirmed(t, w2) n2 := w2.reserved(t, "author@test") p2 := valid(n2) p2.Links = append(p2.Links, LinkAsk{Kind: LinkChanges, To: "0240 §1", From: 2}) w2.accepted(t, "author@test", p2) if _, err := w2.k.Return("operator@shanks", k2, "again"); err == nil || !strings.Contains(err.Error(), "§1 is changed by ADR "+itoa4(n2)+" §2") { t.Fatalf("a record with a changed clause was returned, or the refusal does not name the change: %v", err) } }) }