This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
# Deterministic fixtures and scenarios
|
||||
|
||||
`fixtures/scenarios/` defines failure and recovery behavior without touching the real server.
|
||||
|
||||
Codex must build a telemetry simulator/fake sources capable of:
|
||||
- serving Prometheus-compatible query/metadata responses or a suitable deterministic adapter;
|
||||
- exposing Unraid/inventory snapshots;
|
||||
- emitting normalized events;
|
||||
- changing source freshness;
|
||||
- simulating service probe results;
|
||||
- controlling time for alert tests;
|
||||
- supporting browser end-to-end scenarios.
|
||||
|
||||
Every scenario must validate against `specs/simulator-scenario.schema.json`.
|
||||
|
||||
Production fault tests must use these fixtures or captured redacted read-only data. Never remove disks, degrade arrays, crash real containers or disrupt the network solely for testing.
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "array-degraded",
|
||||
"name": "Array degraded fixture",
|
||||
"description": "Read-only captured/simulated array membership fault.",
|
||||
"initialState": {
|
||||
"array": {
|
||||
"status": "operational",
|
||||
"missingDisks": 0
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{
|
||||
"atSeconds": 30,
|
||||
"action": "set-entity-status",
|
||||
"payload": {
|
||||
"entity": "fixture-array",
|
||||
"status": "critical",
|
||||
"facts": {
|
||||
"missingDisks": 1,
|
||||
"emulatedDisk": "disk3"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{
|
||||
"bySeconds": 60,
|
||||
"assertion": "Array and global status become critical with the missing/emulated disk reason."
|
||||
},
|
||||
{
|
||||
"bySeconds": 60,
|
||||
"assertion": "No repair/start/stop/correct action is offered."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "capacity-forecast",
|
||||
"name": "Capacity forecast confidence states",
|
||||
"description": "A bounded usage history yields a qualified projection while bulk imports, irregular intervals and disabled policy remain explicit without false precision.",
|
||||
"initialState": {
|
||||
"forecasts": {
|
||||
"windowSeconds": 2592000,
|
||||
"maxPoints": 128,
|
||||
"method": "linear_median_rate"
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{"atSeconds": 604800, "action": "set-metric", "payload": {"metric": "storage.share.used_bytes", "entity": "fixture-media", "value": 400000000000}},
|
||||
{"atSeconds": 1209600, "action": "set-metric", "payload": {"metric": "storage.share.used_bytes", "entity": "fixture-media", "value": 500000000000}}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{"bySeconds": 1209600, "assertion": "Forecast response always exposes method, historical window, point count and confidence."},
|
||||
{"bySeconds": 1209600, "assertion": "Bulk-import, irregular-history and insufficient-data states do not expose a projected capacity date."},
|
||||
{"bySeconds": 1209600, "assertion": "Disabled forecasting is rendered as an explicit read-only state rather than as a healthy forecast."}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "container-restart-loop",
|
||||
"name": "Container restart loop",
|
||||
"description": "A container repeatedly restarts while returning to running state.",
|
||||
"initialState": {
|
||||
"container": {
|
||||
"id": "fixture-plex",
|
||||
"name": "Plex",
|
||||
"status": "running",
|
||||
"restartCount": 0
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{
|
||||
"atSeconds": 10,
|
||||
"action": "emit-event",
|
||||
"payload": {
|
||||
"eventType": "container.restart",
|
||||
"entity": "fixture-plex",
|
||||
"exitCode": 137
|
||||
}
|
||||
},
|
||||
{
|
||||
"atSeconds": 120,
|
||||
"action": "emit-event",
|
||||
"payload": {
|
||||
"eventType": "container.restart",
|
||||
"entity": "fixture-plex",
|
||||
"exitCode": 137
|
||||
}
|
||||
},
|
||||
{
|
||||
"atSeconds": 240,
|
||||
"action": "emit-event",
|
||||
"payload": {
|
||||
"eventType": "container.restart",
|
||||
"entity": "fixture-plex",
|
||||
"exitCode": 137
|
||||
}
|
||||
},
|
||||
{
|
||||
"atSeconds": 360,
|
||||
"action": "set-entity-status",
|
||||
"payload": {
|
||||
"entity": "fixture-plex",
|
||||
"status": "operational"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{
|
||||
"bySeconds": 300,
|
||||
"assertion": "Container is degraded due to instability even when current runtime state is running."
|
||||
},
|
||||
{
|
||||
"bySeconds": 330,
|
||||
"assertion": "Restart-loop alert is firing and grouped under the application."
|
||||
},
|
||||
{
|
||||
"bySeconds": 1500,
|
||||
"assertion": "Alert resolves only after the configured stable recovery window."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "database-restart",
|
||||
"name": "Database restart recovery",
|
||||
"description": "Database temporarily restarts while API/worker are active.",
|
||||
"initialState": {
|
||||
"database": {
|
||||
"status": "healthy"
|
||||
},
|
||||
"alerts": {
|
||||
"active": 2
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{
|
||||
"atSeconds": 30,
|
||||
"action": "restart-database",
|
||||
"payload": {
|
||||
"downtimeSeconds": 20
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{
|
||||
"bySeconds": 40,
|
||||
"assertion": "API readiness fails while liveness remains appropriate."
|
||||
},
|
||||
{
|
||||
"bySeconds": 90,
|
||||
"assertion": "Connections recover without duplicate alert transitions or notifications."
|
||||
},
|
||||
{
|
||||
"bySeconds": 120,
|
||||
"assertion": "Persisted dashboards and alert state are intact."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "disk-temperature",
|
||||
"name": "Sustained disk temperature",
|
||||
"description": "Tests pending, firing, hysteresis and recovery.",
|
||||
"initialState": {
|
||||
"disk": {
|
||||
"id": "fixture-disk4",
|
||||
"temperatureC": 38,
|
||||
"smart": "healthy"
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{
|
||||
"atSeconds": 30,
|
||||
"action": "set-metric",
|
||||
"payload": {
|
||||
"metric": "storage.disk.temperature",
|
||||
"entity": "fixture-disk4",
|
||||
"value": 52
|
||||
}
|
||||
},
|
||||
{
|
||||
"atSeconds": 420,
|
||||
"action": "set-metric",
|
||||
"payload": {
|
||||
"metric": "storage.disk.temperature",
|
||||
"entity": "fixture-disk4",
|
||||
"value": 48
|
||||
}
|
||||
},
|
||||
{
|
||||
"atSeconds": 600,
|
||||
"action": "set-metric",
|
||||
"payload": {
|
||||
"metric": "storage.disk.temperature",
|
||||
"entity": "fixture-disk4",
|
||||
"value": 44
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{
|
||||
"bySeconds": 120,
|
||||
"assertion": "Temperature rule is pending, not firing."
|
||||
},
|
||||
{
|
||||
"bySeconds": 360,
|
||||
"assertion": "Temperature alert is firing."
|
||||
},
|
||||
{
|
||||
"bySeconds": 500,
|
||||
"assertion": "Alert remains firing at 48 C because recovery threshold has not been crossed."
|
||||
},
|
||||
{
|
||||
"bySeconds": 930,
|
||||
"assertion": "Alert resolves only after temperature stays below recovery threshold for the configured duration."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "dns-outage-suppression",
|
||||
"name": "DNS outage with downstream suppression",
|
||||
"description": "Many services fail name resolution due to one DNS dependency.",
|
||||
"initialState": {
|
||||
"dns": {
|
||||
"status": "up"
|
||||
},
|
||||
"services": {
|
||||
"up": 20
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{
|
||||
"atSeconds": 30,
|
||||
"action": "set-probe-result",
|
||||
"payload": {
|
||||
"probe": "dns-primary",
|
||||
"success": false,
|
||||
"error": "timeout"
|
||||
}
|
||||
},
|
||||
{
|
||||
"atSeconds": 40,
|
||||
"action": "set-probe-result",
|
||||
"payload": {
|
||||
"probeGroup": "dependent-http",
|
||||
"success": false,
|
||||
"error": "dns"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{
|
||||
"bySeconds": 180,
|
||||
"assertion": "One primary DNS/network incident is created."
|
||||
},
|
||||
{
|
||||
"bySeconds": 180,
|
||||
"assertion": "Downstream service alerts are visible but suppressed/grouped rather than individually notified."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "healthy-baseline",
|
||||
"name": "Healthy baseline",
|
||||
"description": "Stable host, containers, storage and services used as the default deterministic fixture.",
|
||||
"initialState": {
|
||||
"host": {
|
||||
"status": "operational",
|
||||
"cpuPercent": 18,
|
||||
"memoryPercent": 42
|
||||
},
|
||||
"containers": {
|
||||
"running": 70,
|
||||
"stoppedIntentional": 1,
|
||||
"unhealthy": 0
|
||||
},
|
||||
"storage": {
|
||||
"arrayStatus": "operational",
|
||||
"poolUtilizationPercent": 55,
|
||||
"maxDiskTemperatureC": 38
|
||||
},
|
||||
"services": {
|
||||
"up": 25,
|
||||
"down": 0
|
||||
},
|
||||
"sources": {
|
||||
"prometheus": "healthy",
|
||||
"unraid": "healthy",
|
||||
"agent": "healthy"
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{
|
||||
"atSeconds": 0,
|
||||
"action": "set-metric",
|
||||
"payload": {
|
||||
"metric": "host.cpu.utilization",
|
||||
"value": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
"atSeconds": 0,
|
||||
"action": "set-metric",
|
||||
"payload": {
|
||||
"metric": "host.memory.utilization",
|
||||
"value": 42
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{
|
||||
"bySeconds": 10,
|
||||
"assertion": "Global status is operational and contains no active problem reason."
|
||||
},
|
||||
{
|
||||
"bySeconds": 10,
|
||||
"assertion": "All required datasource freshness indicators are fresh."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "pool-capacity-pressure",
|
||||
"name": "Pool capacity pressure",
|
||||
"description": "Cache/pool utilization crosses warning and critical thresholds.",
|
||||
"initialState": {
|
||||
"pool": {
|
||||
"id": "fixture-cache",
|
||||
"utilizationPercent": 70
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{
|
||||
"atSeconds": 30,
|
||||
"action": "set-metric",
|
||||
"payload": {
|
||||
"metric": "storage.pool.utilization",
|
||||
"entity": "fixture-cache",
|
||||
"value": 91
|
||||
}
|
||||
},
|
||||
{
|
||||
"atSeconds": 720,
|
||||
"action": "set-metric",
|
||||
"payload": {
|
||||
"metric": "storage.pool.utilization",
|
||||
"entity": "fixture-cache",
|
||||
"value": 98
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{
|
||||
"bySeconds": 700,
|
||||
"assertion": "Pool is degraded with a capacity reason after sustained 91 percent."
|
||||
},
|
||||
{
|
||||
"bySeconds": 1100,
|
||||
"assertion": "Pool reaches critical according to policy at sustained 98 percent."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "pool-degraded-scrub",
|
||||
"name": "Btrfs pool degraded scrub",
|
||||
"description": "A Btrfs pool keeps its identity and redundancy metadata while one member is missing and scrub reports errors.",
|
||||
"initialState": {
|
||||
"pool": {
|
||||
"id": "fixture-cache",
|
||||
"filesystem": "btrfs",
|
||||
"state": "degraded",
|
||||
"profile": "raid1",
|
||||
"memberStates": ["online", "missing"],
|
||||
"scrub": {"state": "failed", "errors": 3}
|
||||
}
|
||||
},
|
||||
"timeline": [{"atSeconds": 30, "action": "set-metric", "payload": {"metric": "storage.pool.utilization", "entity": "fixture-cache", "value": 91}}],
|
||||
"expectedOutcomes": [
|
||||
{"bySeconds": 0, "assertion": "Pool remains degraded and the missing member is visible."},
|
||||
{"bySeconds": 0, "assertion": "Scrub errors are visible as a warning/critical reason without exposing a scrub control."}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "probe-executor-cases",
|
||||
"name": "Bounded service probe executor cases",
|
||||
"description": "Synthetic cases for successful, failed, timed out, redirected, TLS and unsupported probe execution without real credentials or remote resources.",
|
||||
"initialState": {
|
||||
"networkPolicy": {
|
||||
"allowedNetworks": ["10.0.0.0/8", "127.0.0.0/8"],
|
||||
"maxResponseBytes": 1048576,
|
||||
"maxRedirects": 5
|
||||
},
|
||||
"cases": [
|
||||
{"id": "http-success-json-keyword", "type": "http", "expectedState": "up"},
|
||||
{"id": "http-status-failure", "type": "http", "expectedState": "down"},
|
||||
{"id": "http-timeout", "type": "http", "expectedState": "unknown", "errorClass": "transport_error"},
|
||||
{"id": "http-redirect-policy", "type": "http", "expectedState": "up"},
|
||||
{"id": "http-response-body-limit", "type": "http", "expectedState": "unknown", "errorClass": "response_too_large"},
|
||||
{"id": "tls-certificate-facts", "type": "tls", "expectedState": "up"},
|
||||
{"id": "dns-resolution", "type": "dns", "expectedState": "up"},
|
||||
{"id": "tcp-connect", "type": "tcp", "expectedState": "up"},
|
||||
{"id": "icmp-capability-fallback", "type": "icmp", "expectedState": "unknown", "errorClass": "unsupported"}
|
||||
],
|
||||
"credentialExpectation": "secret references are configuration identifiers only; no plaintext credential is sent, stored or included in result errors"
|
||||
},
|
||||
"timeline": [
|
||||
{"atSeconds": 1, "action": "set-probe-result", "payload": {"resolver": "deterministic", "http": "httptest", "tcp": "net-pipe", "tls": "self-signed-net-pipe"}}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{"bySeconds": 2, "assertion": "HTTP success, status failure, bounded response and per-probe timeout produce the documented states without leaking secret references."},
|
||||
{"bySeconds": 2, "assertion": "Redirects are bounded and revalidated; TLS captures certificate expiry, issuer, subject and hostname validity."},
|
||||
{"bySeconds": 2, "assertion": "TCP and DNS succeed with allowlisted synthetic addresses; ICMP returns Unknown/unsupported when capability is unavailable."}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "probe-scale-300",
|
||||
"name": "Three hundred bounded probes",
|
||||
"description": "A synthetic 300-probe set is scheduled with bounded concurrency, duplicate suppression, cancellation and deterministic result ordering.",
|
||||
"initialState": {"probes": {"count": 300, "maxConcurrent": 16, "maxAttempts": 2}},
|
||||
"timeline": [
|
||||
{"atSeconds": 1, "action": "set-probe-result", "payload": {"probeId": "probe-000", "state": "up", "responseTimeMs": 8}},
|
||||
{"atSeconds": 2, "action": "set-probe-result", "payload": {"probeId": "probe-299", "state": "unknown", "errorClass": "timeout"}}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{"bySeconds": 2, "assertion": "All 300 probes are bounded by the worker pool and results are returned in deterministic probe ID order."},
|
||||
{"bySeconds": 2, "assertion": "A duplicate in-flight probe is skipped rather than overlapping, and timeout/retry attempts remain bounded."}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "prometheus-stale",
|
||||
"name": "Prometheus becomes stale",
|
||||
"description": "Tests that missing current metrics become Unknown rather than remaining healthy.",
|
||||
"initialState": {
|
||||
"sources": {
|
||||
"prometheus": "healthy"
|
||||
},
|
||||
"host": {
|
||||
"status": "operational",
|
||||
"cpuPercent": 20
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{
|
||||
"atSeconds": 0,
|
||||
"action": "set-metric",
|
||||
"payload": {
|
||||
"metric": "host.cpu.utilization",
|
||||
"value": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"atSeconds": 30,
|
||||
"action": "disconnect",
|
||||
"payload": {
|
||||
"source": "prometheus"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{
|
||||
"bySeconds": 90,
|
||||
"assertion": "Prometheus datasource is delayed or stale according to policy."
|
||||
},
|
||||
{
|
||||
"bySeconds": 180,
|
||||
"assertion": "Required metric-derived host state is unknown, not operational."
|
||||
},
|
||||
{
|
||||
"bySeconds": 180,
|
||||
"assertion": "A datasource alert exists without resolving unrelated existing firing alerts."
|
||||
},
|
||||
{
|
||||
"bySeconds": 190,
|
||||
"assertion": "Previous values, if shown, include their age."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "service-down-container-running",
|
||||
"name": "Service down while container runs",
|
||||
"description": "Proves application health is independent from Docker running state.",
|
||||
"initialState": {
|
||||
"container": {
|
||||
"id": "fixture-app",
|
||||
"status": "running"
|
||||
},
|
||||
"service": {
|
||||
"id": "fixture-http",
|
||||
"status": "up",
|
||||
"latencyMs": 80
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{
|
||||
"atSeconds": 30,
|
||||
"action": "set-probe-result",
|
||||
"payload": {
|
||||
"probe": "fixture-http",
|
||||
"success": false,
|
||||
"statusCode": 503,
|
||||
"latencyMs": 150
|
||||
}
|
||||
},
|
||||
{
|
||||
"atSeconds": 240,
|
||||
"action": "set-probe-result",
|
||||
"payload": {
|
||||
"probe": "fixture-http",
|
||||
"success": true,
|
||||
"statusCode": 200,
|
||||
"latencyMs": 90
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{
|
||||
"bySeconds": 150,
|
||||
"assertion": "Service and parent application are degraded while the container remains running."
|
||||
},
|
||||
{
|
||||
"bySeconds": 150,
|
||||
"assertion": "Service alert fires after pending duration."
|
||||
},
|
||||
{
|
||||
"bySeconds": 330,
|
||||
"assertion": "Alert and incident resolve after successful recovery duration."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "share-growth",
|
||||
"name": "Share growth and cache relation",
|
||||
"description": "A bounded share-size history grows across array and cache placement without enumerating file content.",
|
||||
"initialState": {
|
||||
"share": {
|
||||
"id": "fixture-media",
|
||||
"usedBytes": 500000000000,
|
||||
"cachePool": "cache",
|
||||
"primaryPool": "array"
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{"atSeconds": 86400, "action": "set-metric", "payload": {"metric": "storage.share.used_bytes", "entity": "fixture-media", "value": 700000000000}}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{"bySeconds": 86400, "assertion": "Growth history reports a positive 200000000000 byte change per day."},
|
||||
{"bySeconds": 86400, "assertion": "Cache/pool relation remains visible and no file names or content are returned."}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "smart-warning",
|
||||
"name": "SMART warning",
|
||||
"description": "A disk reports pending sectors while overall generic status may still be ambiguous.",
|
||||
"initialState": {
|
||||
"disk": {
|
||||
"id": "fixture-disk5",
|
||||
"smartOverall": "passed",
|
||||
"pendingSectors": 0
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{
|
||||
"atSeconds": 30,
|
||||
"action": "set-entity-status",
|
||||
"payload": {
|
||||
"entity": "fixture-disk5",
|
||||
"status": "attention",
|
||||
"facts": {
|
||||
"pendingSectors": 2,
|
||||
"smartOverall": "passed"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{
|
||||
"bySeconds": 90,
|
||||
"assertion": "Disk status is attention or degraded with an explicit pending-sector reason."
|
||||
},
|
||||
{
|
||||
"bySeconds": 90,
|
||||
"assertion": "UI does not reduce the state to generic SMART passed."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "storage-map-heatmap",
|
||||
"name": "Storage map and temperature heatmap",
|
||||
"description": "A 40-disk storage view remains readable with explicit text states and a table alternative.",
|
||||
"initialState": {"disks": {"count": 40, "temperaturePoints": 40}},
|
||||
"timeline": [{"atSeconds": 60, "action": "set-metric", "payload": {"metric": "storage.disk.temperature", "entity": "disk-7", "value": 52}}],
|
||||
"expectedOutcomes": [
|
||||
{"bySeconds": 60, "assertion": "All 40 disk temperature points remain bounded and the elevated disk has a textual attention state."},
|
||||
{"bySeconds": 60, "assertion": "Storage map and heatmap retain table/text alternatives and drill-down links."}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "ups-on-battery",
|
||||
"name": "UPS on battery",
|
||||
"description": "Optional UPS source changes power state.",
|
||||
"initialState": {
|
||||
"ups": {
|
||||
"available": true,
|
||||
"status": "online",
|
||||
"chargePercent": 100,
|
||||
"runtimeSeconds": 3600
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{
|
||||
"atSeconds": 30,
|
||||
"action": "set-entity-status",
|
||||
"payload": {
|
||||
"entity": "fixture-ups",
|
||||
"status": "attention",
|
||||
"facts": {
|
||||
"powerState": "on-battery",
|
||||
"chargePercent": 96,
|
||||
"runtimeSeconds": 3300
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"atSeconds": 300,
|
||||
"action": "set-entity-status",
|
||||
"payload": {
|
||||
"entity": "fixture-ups",
|
||||
"status": "operational",
|
||||
"facts": {
|
||||
"powerState": "online",
|
||||
"chargePercent": 95,
|
||||
"runtimeSeconds": 3500
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{
|
||||
"bySeconds": 60,
|
||||
"assertion": "UPS is attention with on-battery reason and runtime shown."
|
||||
},
|
||||
{
|
||||
"bySeconds": 360,
|
||||
"assertion": "Recovery is recorded as an event."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "websocket-slow-client",
|
||||
"name": "Slow live client",
|
||||
"description": "A client cannot consume every live sample.",
|
||||
"initialState": {
|
||||
"live": {
|
||||
"subscriptions": 50,
|
||||
"intervalSeconds": 1
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
{
|
||||
"atSeconds": 30,
|
||||
"action": "slow-client",
|
||||
"payload": {
|
||||
"client": "fixture-wallboard",
|
||||
"consumeDelayMs": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"atSeconds": 120,
|
||||
"action": "reconnect",
|
||||
"payload": {
|
||||
"client": "fixture-wallboard"
|
||||
}
|
||||
}
|
||||
],
|
||||
"expectedOutcomes": [
|
||||
{
|
||||
"bySeconds": 60,
|
||||
"assertion": "Server coalesces/drops intermediate non-critical samples without unbounded queue growth."
|
||||
},
|
||||
{
|
||||
"bySeconds": 140,
|
||||
"assertion": "Client resynchronizes using sequence/status protocol."
|
||||
},
|
||||
{
|
||||
"bySeconds": 180,
|
||||
"assertion": "Subscription count returns to expected value with no duplicate streams."
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user