Skip to content
quarry Artifact [66f35e8cad]
Login

Artifact [66f35e8cad]

Artifact 66f35e8cad1cd47f37f6232bedc29a86b5d5db66ce860d271d3f15e6f91d6d8f:


package oltp

import (
	"strings"
	"testing"
)

// The sentence and the letters must answer the same question.
//
// account.explain gave one answer built from two rules. Its `can`
// sentence went through Explain, which asks CapSet.HasAny — the
// function every GATE goes through, and which reads `a` as a wildcard
// for everything except s, x and y. Its letter list went through
// Decode, which asks Closure, and Closure has no entry for `a` at all.
//
// So a repository admin was told they could check in and edit tickets,
// above a letter list showing no i, k, w or f. Both halves were
// internally consistent and the answer as a whole contradicted itself
// (8ea1e46d, G-84).
//
// This is the invariant: for any capability string, every ability the
// gate would PERMIT must be an ability the letters ADMIT. Stated over
// the abilities rather than over one example, so a new letter or a new
// implication cannot slip between the two halves again.
func TestWhatTheGatePermitsIsWhatTheLettersAdmit(t *testing.T) {
	for _, caps := range []string{"", "a", "i", "oi", "w", "k", "ogzh", "ai", "3", "C", "8"} {
		gated := CapSet(AsGated(caps))
		for _, ab := range Abilities {
			// What a gate would answer for this ability's own letter.
			permits := CapSet(caps).HasAny(ab.Letters)
			admits := gated.HasAny(ab.Letters)
			if permits && !admits {
				t.Errorf("FAIL: with caps %q a gate permits %q but the letters do not admit "+
					"it; the sentence and the list disagree", caps, ab.Sentence)
			}
			// Both directions, now that dmestas has decided we
			// follow fossil and HasAny consults the implies table.
			//
			// This assertion used to be impossible in one direction:
			// HasAny knew the s and a wildcards and not the implies
			// table, Closure knew implies and not the wildcards, so
			// each admitted things the other refused. The letters
			// admitted reads that gates denied (w without r) and the
			// sentence claimed powers the letters omitted (a without
			// i). One rule, so both hold.
			if admits && !permits {
				t.Errorf("FAIL: with caps %q the letters admit %q that no gate permits; the "+
					"list overstates what the account may do", caps, ab.Sentence)
			}
		}
	}
}

// And `a` expands to the gate's rule exactly: everything but s, x, y.
//
// Excluded deliberately and for different reasons: s is SITE admin and
// is not a repository letter at all, and fossil keeps private-branch
// (x) and unversioned-write (y) out of `a` on purpose. Getting this
// list wrong in the generous direction would have the page tell a
// repository admin they can read private branches.
func TestTheAdminLetterExpandsToTheGatesRuleAndNoFurther(t *testing.T) {
	g := AsGated("a")
	for _, l := range "sxy" {
		if strings.ContainsRune(g, l) {
			t.Errorf("FAIL: `a` expanded to include %q, which HasAny excludes; the page "+
				"would claim a power no gate grants", l)
		}
	}
	for _, want := range "iwkfn" {
		if !strings.ContainsRune(g, want) {
			t.Errorf("FAIL: `a` did not expand to %q, which HasAny grants", want)
		}
	}
	// A non-admin set is untouched: this changes what an admin is TOLD,
	// never what anybody else holds.
	for _, caps := range []string{"i", "oi", "w", "ogzh", "3"} {
		if AsGated(caps) != Closure(caps) {
			t.Errorf("FAIL: caps %q changed under AsGated (%q vs %q); only `a` should expand",
				caps, AsGated(caps), Closure(caps))
		}
	}
}