[
{"name": "three one-minute buckets", "args": [[{"at": 0, "value": 5}, {"at": 30, "value": 7}, {"at": 60, "value": 3}, {"at": 90, "value": 9}, {"at": 150, "value": 4}], 0, 180, 60], "expect": [{"start": 0, "end": 60, "count": 2, "min": 5, "max": 7, "avg": 6, "sum": 12, "last": 7}, {"start": 60, "end": 120, "count": 2, "min": 3, "max": 9, "avg": 6, "sum": 12, "last": 9}, {"start": 120, "end": 180, "count": 1, "min": 4, "max": 4, "avg": 4, "sum": 4, "last": 4}]},
{"name": "an empty bucket in the middle is kept, so a chart shows the gap", "args": [[{"at": 0, "value": 1}, {"at": 130, "value": 2}], 0, 180, 60], "expect": [{"start": 0, "end": 60, "count": 1, "min": 1, "max": 1, "avg": 1, "sum": 1, "last": 1}, {"start": 60, "end": 120, "count": 0, "min": null, "max": null, "avg": null, "sum": 0, "last": null}, {"start": 120, "end": 180, "count": 1, "min": 2, "max": 2, "avg": 2, "sum": 2, "last": 2}]},
{"name": "the last bucket is clipped to to", "args": [[{"at": 10, "value": 4}, {"at": 70, "value": 6}, {"at": 99, "value": 8}], 0, 100, 60], "expect": [{"start": 0, "end": 60, "count": 1, "min": 4, "max": 4, "avg": 4, "sum": 4, "last": 4}, {"start": 60, "end": 100, "count": 2, "min": 6, "max": 8, "avg": 7, "sum": 14, "last": 8}]},
{"name": "a sample exactly at to is outside the window", "args": [[{"at": 0, "value": 1}, {"at": 120, "value": 5}], 0, 120, 60], "expect": [{"start": 0, "end": 60, "count": 1, "min": 1, "max": 1, "avg": 1, "sum": 1, "last": 1}, {"start": 60, "end": 120, "count": 0, "min": null, "max": null, "avg": null, "sum": 0, "last": null}]},
{"name": "averages round half up, halves away from zero: 1.5 is 2 and -1.5 is -2", "args": [[{"at": 0, "value": 1}, {"at": 1, "value": 2}, {"at": 10, "value": -1}, {"at": 11, "value": -2}], 0, 20, 10], "expect": [{"start": 0, "end": 10, "count": 2, "min": 1, "max": 2, "avg": 2, "sum": 3, "last": 2}, {"start": 10, "end": 20, "count": 2, "min": -2, "max": -1, "avg": -2, "sum": -3, "last": -2}]},
{"name": "buckets are aligned to from, and samples outside the window are ignored", "args": [[{"at": 0, "value": 100}, {"at": 50, "value": 1}, {"at": 104, "value": 3}, {"at": 105, "value": 5}, {"at": 200, "value": 9}], 45, 165, 60], "expect": [{"start": 45, "end": 105, "count": 2, "min": 1, "max": 3, "avg": 2, "sum": 4, "last": 3}, {"start": 105, "end": 165, "count": 1, "min": 5, "max": 5, "avg": 5, "sum": 5, "last": 5}]},
{"name": "last is the latest sample, not the largest", "args": [[{"at": 0, "value": 9}, {"at": 5, "value": 2}], 0, 10, 10], "expect": [{"start": 0, "end": 10, "count": 2, "min": 2, "max": 9, "avg": 6, "sum": 11, "last": 2}]},
{"name": "a bucket wider than the window is one clipped bucket", "args": [[{"at": 5, "value": 3}], 0, 30, 60], "expect": [{"start": 0, "end": 30, "count": 1, "min": 3, "max": 3, "avg": 3, "sum": 3, "last": 3}]},
{"name": "an empty window has no buckets", "args": [[{"at": 5, "value": 3}], 100, 100, 60], "expect": []},
{"name": "no samples gives empty buckets, not no buckets", "args": [[], 0, 120, 60], "expect": [{"start": 0, "end": 60, "count": 0, "min": null, "max": null, "avg": null, "sum": 0, "last": null}, {"start": 60, "end": 120, "count": 0, "min": null, "max": null, "avg": null, "sum": 0, "last": null}]},
{"name": "a zero-width bucket is an error", "args": [[], 0, 60, 0], "expectError": "bucketSeconds must be at least 1, received 0"},
{"name": "a fractional bucket width is an error", "args": [[], 0, 60, 1.5], "expectError": "bucketSeconds must be a whole number of seconds"},
{"name": "more than 10000 buckets is an error", "args": [[], 0, 10001, 1], "expectError": "too many buckets: 10001, at most 10000"},
{"name": "from after to is an error", "args": [[], 200, 100, 60], "expectError": "from must not be after to: 200 > 100"},
{"name": "misordered samples are an error", "args": [[{"at": 10, "value": 1}, {"at": 5, "value": 2}], 0, 60, 60], "expectError": "samples must be in strictly ascending time order: 5 follows 10"}
]