Assert Syntax
GLUT asserts check the result after the pipeline run. They are grouped by resource type:
job: GitLab CI jobs and logs.artifacts: files in the workspace.git: workspace and fake origin git state.api: calls recorded by the mock GitLab API.binary: calls recorded by mock binaries.
Most assert fields accept either an exact value or a matcher object. A matcher object has one operator key.
exit-status: 0
branch:
have-prefix: "release/"
commits:
ge: 2
Operator Reference
Use the same operators across job, artifact, git, API, and binary asserts.
Equality And Text Operators
| Operator | Value type | Actual value | Meaning | Example |
|---|---|---|---|---|
equal |
any | any | Exact value match. | equal: "main" |
have-prefix |
string | string | Actual string starts with value. | have-prefix: "feat/" |
have-suffix |
string | string | Actual string ends with value. | have-suffix: ".json" |
contain-substring |
string | string | Actual string contains value. | contain-substring: "ready" |
match-regexp |
string | string | Actual string matches a regexp. | match-regexp: "^v[0-9]+" |
semver-constraint |
string | string | Actual value is a semver version in range. | semver-constraint: ">=1.2.0" |
Example:
assert:
git:
origin:
last-commit:
message:
contain-substring: "update manifest"
sha:
match-regexp: "^[a-f0-9]{40}$"
Number Operators
| Operator | Value type | Actual value | Meaning | Example |
|---|---|---|---|---|
gt |
number | number | Actual value is greater than value. | gt: 0 |
ge |
number | number | Actual value is greater than or equal to value. | ge: 1 |
lt |
number | number | Actual value is less than value. | lt: 10 |
le |
number | number | Actual value is less than or equal to value. | le: 10 |
Example:
assert:
artifacts:
"dist/result.json":
size:
gt: 0
binary:
release-cli:
times:
ge: 1
Collection Operators
| Operator | Value type | Actual value | Meaning | Example |
|---|---|---|---|---|
contain-element |
any | list | At least one item matches value. | contain-element: "create" |
contain-elements |
list | list | All listed items are present. | contain-elements: ["linux", "amd64"] |
consist-of |
list | list | Lists contain the same values. Order does not matter. | consist-of: ["a", "b"] |
have-len |
integer | list, map, or string | Actual value has this length. | have-len: 2 |
have-key |
string | map | Actual map has this key. | have-key: "tag_name" |
Example:
assert:
binary:
docker:
calls:
- args:
contain-elements:
- "build"
- "--pull"
- "."
JSON Operators
| Operator | Value type | Actual value | Meaning | Example |
|---|---|---|---|---|
gjson |
map | JSON text or object | Read values with gjson paths and match them. | "items.#": { ge: 1 } |
Use gjson for JSON files and API request bodies.
assert:
artifacts:
"dist/manifest.json":
contents:
gjson:
"image.name": "registry.example.com/app"
"image.tag":
match-regexp: "^v[0-9]+\\.[0-9]+\\.[0-9]+$"
"platforms.#":
ge: 2
Logic Operators
| Operator | Value type | Actual value | Meaning | Example |
|---|---|---|---|---|
and |
list | any | All child matchers must pass. | and: [{ have-prefix: "v" }, { semver-constraint: ">=1.0.0" }] |
or |
list | any | At least one child matcher must pass. | or: ["main", "master"] |
not |
matcher | any | Child matcher must fail. | not: { contain-substring: "dirty" } |
Example:
assert:
git:
workspace:
branch:
or:
- "main"
- "master"
last-commit:
message:
and:
- have-prefix: "chore:"
- not:
contain-substring: "WIP"
Text Pattern Lists
stdout, stderr, and some contents fields can use a list of text patterns.
Each item is checked against the actual text.
| Pattern | Meaning |
|---|---|
plain text |
Text must contain this string. |
/regexp/ |
Text must match this regexp. |
!/regexp/ |
Text must not match this regexp. |
\!text |
Text must contain a value that starts with !. |
Example:
assert:
job:
release:
stdout:
- "created release"
- "/tag: v[0-9]+\\.[0-9]+\\.[0-9]+/"
- "!/panic|fatal/"
Job Asserts
assert.job checks job presence, exit status, stdout, and stderr. The key is
the GitLab CI job name.
| Field | Type | Meaning |
|---|---|---|
present |
boolean | Job must exist or must not exist. |
exit-status |
value or matcher | Process exit status. |
stdout |
text list or matcher | Standard output. |
stderr |
text list or matcher | Standard error. |
Basic success:
assert:
job:
build:
present: true
exit-status: 0
stdout:
- "build complete"
Expected failure:
assert:
job:
validate:
exit-status: 1
stderr:
- "missing tag"
- "!/panic|stack trace/"
Skipped or missing job:
assert:
job:
deploy-production:
present: false
Flexible log check:
assert:
job:
package:
stdout:
contain-substring: "archive created"
stderr:
not:
contain-substring: "warning"
Artifact Asserts
assert.artifacts checks files in the workspace. The key is the relative path.
| Field | Type | Meaning |
|---|---|---|
exists |
boolean | Path must exist or must not exist. |
contents |
text list or matcher | File content. |
mode |
string | File permission mode as four octal digits, such as 0644. |
size |
value or matcher | File size in bytes. |
md5 |
string | Expected MD5 hash. |
sha256 |
string | Expected SHA-256 hash. |
filetype |
string | file, directory, symlink, or socket. |
Text file:
assert:
artifacts:
"dist/image.txt":
exists: true
filetype: "file"
contents:
- "registry.example.com/app"
- "/tag: feature-[a-z0-9-]+/"
JSON file:
assert:
artifacts:
"dist/manifest.json":
exists: true
contents:
gjson:
"image.name": "registry.example.com/app"
"image.tag":
have-prefix: "v"
"assets.#":
ge: 1
File metadata:
assert:
artifacts:
"dist/tool":
exists: true
mode: "0755"
size:
gt: 1024
sha256: "3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7"
Absent file:
assert:
artifacts:
"dist/debug.log":
exists: false
Git Asserts
assert.git checks the workspace repository and the fake origin repository.
assert:
git:
workspace:
clean: true
origin:
commits:
ge: 2
Fields for workspace and origin:
| Field | Type | Meaning |
|---|---|---|
commits |
value or matcher | Commit count. |
last-commit |
object | Last commit metadata. |
file |
map | Files in the repository. |
branch |
value or matcher | Current branch. Workspace only. |
clean |
boolean | Workspace has no changes. Workspace only. |
Fields for last-commit:
| Field | Type | Meaning |
|---|---|---|
author-name |
value or matcher | Last commit author name. |
author-email |
value or matcher | Last commit author email. |
message |
value or matcher | Last commit message. |
sha |
value or matcher | Last commit SHA. |
Fields for file entries (key is the path relative to the repository root):
| Field | Type | Meaning |
|---|---|---|
exists |
boolean | File must exist or must not exist. |
contents |
text list or matcher | File content. |
mode |
string | File permission mode as four octal digits, such as 0644. |
size |
value or matcher | File size in bytes. |
md5 |
string | Expected MD5 hash. |
sha256 |
string | Expected SHA-256 hash. |
filetype |
string | file, directory, symlink, or socket. |
These fields are identical to assert.artifacts fields.
Check a pushed manifest update:
assert:
git:
origin:
commits:
ge: 2
last-commit:
author-email: "test@example.com"
message:
contain-substring: "update manifest"
file:
"manifest.yaml":
contents:
contain-substring: "image: new"
Check the workspace state:
assert:
git:
workspace:
branch: "main"
clean: true
Check a commit SHA shape:
assert:
git:
origin:
last-commit:
sha:
match-regexp: "^[a-f0-9]{40}$"
Check a file was deleted by the pipeline:
assert:
git:
origin:
file:
"CHANGELOG.md":
exists: false
"dist/release.json":
exists: true
contents:
- "/\"version\": \"v[0-9]+/"
API Asserts
assert.api checks calls recorded by the mock GitLab API. The key is
METHOD path. Use * to match a path segment.
| Field | Type | Meaning |
|---|---|---|
called |
boolean | Request must happen or must not happen. |
times |
value or matcher | Number of matching requests. |
body |
map | Request JSON body matcher. |
Release creation:
assert:
api:
"POST /api/v4/projects/*/releases":
called: true
times: 1
body:
tag_name: "v1.2.0"
name:
contain-substring: "v1.2.0"
Merge request comment:
assert:
api:
"POST /api/v4/projects/*/merge_requests/*/notes":
called: true
body:
body:
contain-substring: "ready"
No production call:
assert:
api:
"DELETE /api/v4/projects/*/releases/*":
called: false
Nested JSON body:
assert:
api:
"POST /api/v4/projects/*/releases":
body:
gjson:
"assets.links.#":
ge: 1
"assets.links.0.name":
have-suffix: ".tar.gz"
Binary Asserts
assert.binary checks calls to mock binaries. The key is the binary name.
| Field | Type | Meaning |
|---|---|---|
called |
boolean | Binary must be called or must not be called. |
times |
value or matcher | Number of calls. |
calls |
list | Ordered call expectations. |
never-called-with |
object | A call shape that must not appear. |
Call fields:
| Field | Type | Meaning |
|---|---|---|
args |
value or matcher | Process arguments. |
cwd |
value or matcher | Working directory. |
stdin |
value or matcher | Standard input. |
Release CLI call:
assert:
binary:
release-cli:
called: true
times: 1
calls:
- args:
contain-elements:
- "create"
- "--tag-name"
- "v1.2.0"
cwd:
have-suffix: "/builds"
Docker build call:
assert:
binary:
docker:
called: true
calls:
- args:
and:
- contain-element: "build"
- contain-element: "--pull"
- contain-element: "."
Command must not use a flag:
assert:
binary:
release-cli:
never-called-with:
args:
contain-element: "--dry-run"
Check stdin:
assert:
binary:
kubectl:
calls:
- args:
contain-elements:
- "apply"
- "-f"
- "-"
stdin:
contain-substring: "apiVersion:"
Full Example
This example combines job, artifact, git, API, and binary asserts.
assert:
job:
release:
exit-status: 0
stdout:
- "release-cli create"
- "!/panic|fatal/"
artifacts:
"dist/release.json":
exists: true
contents:
gjson:
"tag": "v1.2.0"
git:
workspace:
clean: true
api:
"POST /api/v4/projects/*/releases":
called: true
times: 1
binary:
release-cli:
called: true
calls:
- args:
contain-elements:
- "create"
- "v1.2.0"