In Gatekeeper's gator verify, what can a Suite case assert beyond a plain denial?
answer
- the checked-in fixture file, not a flag
- one test per template and constraint
- cases pin more than yes or no
- count and message, not just denial
- referential rules need fixture inventory
basics
~20 sA gator verify case can assert how many violations the object produced and what the violation message said, not merely that something was denied. That pins which rule fired and what a developer will be told when it does.
solid answer
~50 s`gator verify` is driven by a `Suite` file (`apiVersion: test.gatekeeper.sh/v1alpha1`, `kind: Suite`). Each entry under `tests` names one `template` and one `constraint`; each `case` under it names an `object` file and a list of `assertions`. An assertion can set `violations` to `yes`, `no`, or an exact number, and can pin `message` so the violation text must match what you expect. That matters because `violations: yes` passes for any denial — including a denial for the wrong reason, from the wrong branch of the rule, or a rule that fires twice per object when you meant once. Cases can also list `inventory` files, which is how you give a rule that needs to see other objects the state it would otherwise read from the cluster. The suite is a file in the rule repository, so the whole thing runs in CI with no cluster.
code
yaml · 17 linesapiVersion: test.gatekeeper.sh/v1alpha1
kind: Suite
tests:
- name: require-pdb-above-one-replica
template: template.yaml
constraint: constraint.yaml
cases:
- name: three-replicas-no-budget
object: deploy-3-replicas.yaml
inventory: [existing-pdbs.yaml]
assertions:
- violations: 1
message: PodDisruptionBudget
- name: single-replica-allowed
object: deploy-1-replica.yaml
assertions:
- violations: nogo deeper
Know that the suite is a checked-in YAML file describing tests and cases, that each case points at one object manifest, and that a case asserts an expected outcome rather than just running the rule.
Be able to walk the Suite structure — tests naming a template and constraint, cases naming an object and assertions — and explain why an exact violation count and a message assertion catch bugs that a bare denial check misses.
Demonstrate suite design judgment: allow cases alongside deny cases, message assertions on the paths developers hit, and fixture inventory for referential rules so a deny never passes because the rule saw nothing.
Argue for the standard the organisation holds rule changes to. If a constraint may only merge with a suite that pins messages and covers the allow path, guardrail quality stops depending on who wrote it.
## The Suite file Where `gator test` answers "do these manifests pass these rules right now", `gator verify` is the assertion harness: a checked-in test suite for a constraint, written as a Kubernetes-shaped document. A `Suite` (`apiVersion: test.gatekeeper.sh/v1alpha1`, `kind: Suite`) holds a list of `tests`. Each test names the `template` and `constraint` files it exercises, and holds a list of `cases`. Each case names an `object` file — one manifest to review — and a list of `assertions` about the result. Paths are relative to the suite file, so a suite plus its fixture files is a self-contained directory in the rule repository. ## What an assertion can pin - **`violations`** — `no` for the allow case, `yes` for a denial, or an integer when the count itself is the thing you care about. A rule that walks a list of containers can easily produce one violation per container; asserting an exact count is how you notice it produced three when the object has one offending container. - **`message`** — the violation's message must match the text you give. This is the assertion that turns "something was rejected" into "the rule I meant rejected it, for the reason I meant, with wording a developer can act on". A case may carry more than one assertion, which is how you say "exactly one violation, and its message mentions the PodDisruptionBudget". ## Why `violations: yes` alone is a weak test Consider the availability rule: any Deployment above one replica must be covered by a PodDisruptionBudget. Suppose a refactor introduces a typo in the parameter lookup, and the rule now denies every Deployment it sees, whatever its replica count and whatever budgets exist. Every deny case still passes. The suite goes green. Only the allow case — a single-replica Deployment that must not be flagged — and a message assertion catch it, because the message the broken rule emits is not the message you asserted. The same trap appears when one template grows several distinct denials. Without a message assertion, a case can pass because a completely different branch of the rule fired, and you have unknowingly stopped testing the branch you thought you were testing. ## Cases that need cluster state An admission review carries the object under review and nothing else — no siblings, no cluster inventory, no history. A rule like the PodDisruptionBudget one is *referential*: to decide, it needs to know which budgets exist. In a cluster that state is replicated into the engine; offline it has to come from fixtures, and a case's `inventory` field is where those manifests go. That makes the referential case honest in both directions: one fixture set where a matching budget exists (allow) and one where it does not (deny). Forget the inventory and your "deny" case may pass for the wrong reason — the rule found nothing because you gave it nothing. ## Suite hygiene - Give every test and case a `name`. When CI fails, the name is the whole error message a reviewer reads first. - Keep the allow cases. A suite of deny cases only proves the rule can say no, never that it can say yes, and a rule that denies everything is the fastest way to have your guardrail switched off by an angry platform team. - Assert the message on the cases a human will actually hit. The message is the product: it is what appears in a developer's terminal at 5pm when their deploy is rejected. - Keep the suite next to the template and constraint it tests, so a change to the rule and a change to its proof arrive in the same review. ## Where it runs `gator verify` over the suite directory is a pre-merge check on the rule repository. No cluster, no kubeconfig, no credentials: the suite, the fixtures and the rules are all files. That is what lets a constraint change be reviewed like ordinary code — with a diff, a test run and a red build when the proof breaks.
- Why assert the message when the case already asserts that a violation happened?Because a denial is not evidence that your rule denied. Another branch of the template, or a rule broken into denying everything, also produces a violation and still passes `violations: yes`. Pinning the message ties the case to one specific behaviour, and it also regression-tests the wording the blocked developer sees.
- How do you test a constraint that has to know whether some other object exists?Supply that state as fixtures. A case's `inventory` field takes manifest files that stand in for the cluster objects the rule would otherwise read, so you can write one case where the required object exists and one where it does not. Offline evaluation invents no state — whatever you do not provide, the rule does not see.
- What is the value of keeping an allow case when the rule's job is to deny?It is the only thing standing between you and a rule that denies everything. Deny cases pass just as happily against a broken rule that rejects all input; the allow case is what fails, and it is also the case that models the change your colleagues make every day.
saying these in an interview costs you the question
- Asserts only violations: yes, so any denial passes
- Writes deny cases only and never an allow case
- Thinks gator verify contacts a cluster to run the suite
- Assumes violations: yes means exactly one violation
- Expects a referential rule to see objects no fixture provided