Functional Weave
Code in Python

monitor.alert-rule@1.0.0

vectors.json

7,547 bytes · view raw

[
  {"name": "a breach held for the whole for-duration is firing", "args": [[{"at": 0, "value": 50}, {"at": 60, "value": 95}, {"at": 120, "value": 97}, {"at": 180, "value": 99}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 120, "staleAfterSeconds": null}, 180], "expect": {"state": "firing", "since": 60, "value": 99, "heldSeconds": 120}},
  {"name": "one second short of the for-duration is still pending", "args": [[{"at": 0, "value": 50}, {"at": 60, "value": 95}, {"at": 120, "value": 97}, {"at": 180, "value": 99}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 120, "staleAfterSeconds": null}, 179], "expect": {"state": "pending", "since": 60, "value": 97, "heldSeconds": 119}},
  {"name": "forSeconds 0 fires on the first breaching sample", "args": [[{"at": 0, "value": 50}, {"at": 60, "value": 95}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": null}, 60], "expect": {"state": "firing", "since": 60, "value": 95, "heldSeconds": 0}},
  {"name": "below the threshold is inactive", "args": [[{"at": 0, "value": 50}, {"at": 60, "value": 80}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 60, "staleAfterSeconds": null}, 60], "expect": {"state": "inactive", "since": null, "value": 80, "heldSeconds": 0}},
  {"name": "gt: a value exactly at the threshold is not a breach", "args": [[{"at": 0, "value": 90}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": null}, 0], "expect": {"state": "inactive", "since": null, "value": 90, "heldSeconds": 0}},
  {"name": "gte: a value exactly at the threshold is a breach", "args": [[{"at": 0, "value": 90}], {"op": "gte", "threshold": 90, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": null}, 0], "expect": {"state": "firing", "since": 0, "value": 90, "heldSeconds": 0}},
  {"name": "hysteresis: dipping below the threshold but above the clear level keeps the breach", "args": [[{"at": 0, "value": 95}, {"at": 60, "value": 85}, {"at": 120, "value": 85}], {"op": "gt", "threshold": 90, "clearThreshold": 80, "forSeconds": 60, "staleAfterSeconds": null}, 120], "expect": {"state": "firing", "since": 0, "value": 85, "heldSeconds": 120}},
  {"name": "hysteresis: reaching the clear level clears, and a value under the threshold does not re-breach", "args": [[{"at": 0, "value": 95}, {"at": 60, "value": 80}, {"at": 120, "value": 85}], {"op": "gt", "threshold": 90, "clearThreshold": 80, "forSeconds": 60, "staleAfterSeconds": null}, 120], "expect": {"state": "inactive", "since": null, "value": 85, "heldSeconds": 0}},
  {"name": "a new breach after a clear restarts the clock, it does not reuse the old start", "args": [[{"at": 0, "value": 95}, {"at": 60, "value": 70}, {"at": 120, "value": 95}, {"at": 180, "value": 96}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 120, "staleAfterSeconds": null}, 200], "expect": {"state": "pending", "since": 120, "value": 96, "heldSeconds": 80}},
  {"name": "lt with a clear level above: low disk space stays breaching until it recovers past 20", "args": [[{"at": 0, "value": 15}, {"at": 60, "value": 8}, {"at": 120, "value": 12}], {"op": "lt", "threshold": 10, "clearThreshold": 20, "forSeconds": 60, "staleAfterSeconds": null}, 150], "expect": {"state": "firing", "since": 60, "value": 12, "heldSeconds": 90}},
  {"name": "lt: recovering past the clear level is inactive", "args": [[{"at": 0, "value": 15}, {"at": 60, "value": 8}, {"at": 120, "value": 12}, {"at": 180, "value": 25}], {"op": "lt", "threshold": 10, "clearThreshold": 20, "forSeconds": 60, "staleAfterSeconds": null}, 180], "expect": {"state": "inactive", "since": null, "value": 25, "heldSeconds": 0}},
  {"name": "lte: a value at the threshold breaches and waits out the for-duration", "args": [[{"at": 0, "value": 10}], {"op": "lte", "threshold": 10, "clearThreshold": null, "forSeconds": 30, "staleAfterSeconds": null}, 20], "expect": {"state": "pending", "since": 0, "value": 10, "heldSeconds": 20}},
  {"name": "no samples at all is no data", "args": [[], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": null}, 100], "expect": {"state": "no-data", "since": null, "value": null, "heldSeconds": 0}},
  {"name": "samples only after now are ignored, so there is no data yet", "args": [[{"at": 100, "value": 99}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": null}, 50], "expect": {"state": "no-data", "since": null, "value": null, "heldSeconds": 0}},
  {"name": "samples after now are ignored", "args": [[{"at": 0, "value": 95}, {"at": 60, "value": 10}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": null}, 30], "expect": {"state": "firing", "since": 0, "value": 95, "heldSeconds": 30}},
  {"name": "a latest sample older than staleAfterSeconds is no data, reporting the stale value", "args": [[{"at": 0, "value": 99}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": 60}, 61], "expect": {"state": "no-data", "since": null, "value": 99, "heldSeconds": 0}},
  {"name": "a latest sample exactly staleAfterSeconds old still counts", "args": [[{"at": 0, "value": 99}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": 60}, 60], "expect": {"state": "firing", "since": 0, "value": 99, "heldSeconds": 60}},
  {"name": "negative values breach an lt rule", "args": [[{"at": 0, "value": -5}], {"op": "lt", "threshold": 0, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": null}, 0], "expect": {"state": "firing", "since": 0, "value": -5, "heldSeconds": 0}},
  {"name": "samples out of time order are an error", "args": [[{"at": 60, "value": 1}, {"at": 0, "value": 2}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": null}, 100], "expectError": "samples must be in strictly ascending time order"},
  {"name": "a clear level above a gt threshold is an error", "args": [[{"at": 0, "value": 1}], {"op": "gt", "threshold": 90, "clearThreshold": 95, "forSeconds": 0, "staleAfterSeconds": null}, 0], "expectError": "clearThreshold must be on the non-breaching side of threshold: gt 90, clear 95"},
  {"name": "a clear level below an lt threshold is an error", "args": [[{"at": 0, "value": 1}], {"op": "lt", "threshold": 10, "clearThreshold": 5, "forSeconds": 0, "staleAfterSeconds": null}, 0], "expectError": "clearThreshold must be on the non-breaching side of threshold: lt 10, clear 5"},
  {"name": "a negative for-duration is an error", "args": [[{"at": 0, "value": 1}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": -1, "staleAfterSeconds": null}, 0], "expectError": "forSeconds must not be negative"},
  {"name": "a negative stale limit is an error", "args": [[{"at": 0, "value": 1}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": -1}, 0], "expectError": "staleAfterSeconds must not be negative"},
  {"name": "an unknown comparison is an error", "args": [[{"at": 0, "value": 1}], {"op": "eq", "threshold": 90, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": null}, 0], "expectError": "unknown comparison: eq"},
  {"name": "a fractional now is an error", "args": [[{"at": 0, "value": 1}], {"op": "gt", "threshold": 90, "clearThreshold": null, "forSeconds": 0, "staleAfterSeconds": null}, 1.5], "expectError": "now must be a whole number of seconds"}
]