mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-11 23:04:52 +09:00
The a and A felt lonely being the only letters included. Add the other letters in the ASCII character set as there was no good reason to leave them out.
1031 lines
33 KiB
JavaScript
1031 lines
33 KiB
JavaScript
"use strict";
|
|
const assert = require("assert").strict;
|
|
|
|
const prometheus = require("@clusterio/lib");
|
|
|
|
|
|
describe("lib/prometheus", function() {
|
|
describe("class CollectorRegistry", function() {
|
|
let registry;
|
|
beforeEach(function() {
|
|
registry = new prometheus.CollectorRegistry();
|
|
});
|
|
|
|
describe(".register()", function() {
|
|
it("should register a collector", function() {
|
|
let collector = new prometheus.Collector(false);
|
|
registry.register(collector);
|
|
assert(
|
|
registry.collectors.includes(collector),
|
|
"Collector was not registered"
|
|
);
|
|
});
|
|
it("should throw when already registered", function() {
|
|
let collector = new prometheus.Collector(false);
|
|
registry.register(collector);
|
|
assert.throws(
|
|
() => registry.register(collector),
|
|
new Error("Collector is already registered in this registry.")
|
|
);
|
|
});
|
|
it("should allow being regitstered in another register", function() {
|
|
let otherCollectorRegistry = new prometheus.CollectorRegistry();
|
|
let collector = new prometheus.Collector(false);
|
|
registry.register(collector);
|
|
otherCollectorRegistry.register(collector);
|
|
assert(
|
|
registry.collectors.includes(collector),
|
|
"Collector was not registered in first register"
|
|
);
|
|
assert(
|
|
otherCollectorRegistry.collectors.includes(collector),
|
|
"Collector was not registered in second register"
|
|
);
|
|
});
|
|
});
|
|
describe(".unregister()", function() {
|
|
it("should removed a registered collector", function() {
|
|
let collector = new prometheus.Collector(false);
|
|
registry.register(collector);
|
|
registry.unregister(collector);
|
|
assert(
|
|
!registry.collectors.includes(collector),
|
|
"Collector was not removed"
|
|
);
|
|
});
|
|
it("should throw when not registered", function() {
|
|
let collector = new prometheus.Collector(false);
|
|
assert.throws(
|
|
() => registry.unregister(collector),
|
|
new Error("Collector is not registered in this registry.")
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("defaultRegistry", function() {
|
|
it("should have collectors registered to it by default", function() {
|
|
let collector = new prometheus.Collector();
|
|
assert(
|
|
prometheus.defaultRegistry.collectors.includes(collector),
|
|
"collector was not registered to defaultRegistry"
|
|
);
|
|
prometheus.defaultRegistry.unregister(collector);
|
|
});
|
|
it("should have the default collectors registered to it", function() {
|
|
let collectors = Object.entries(prometheus.defaultCollectors);
|
|
for (let [name, collector] of collectors) {
|
|
assert(
|
|
prometheus.defaultRegistry.collectors.includes(collector),
|
|
`${name} is not registered`
|
|
);
|
|
}
|
|
});
|
|
it("should allow default collectors to be unregistered", function() {
|
|
this.skip(); // XXX Doesn't work in Clusterio's shared testing env
|
|
let collectors = Object.values(prometheus.defaultCollectors);
|
|
for (let collector of collectors) {
|
|
prometheus.defaultRegistry.unregister(collector);
|
|
}
|
|
assert(
|
|
prometheus.defaultRegistry.collectors.length === 0,
|
|
"Collectors left over after removing the default ones"
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("class Collector", function() {
|
|
it("should implement .collect()", async function() {
|
|
let collector = new prometheus.Collector(false);
|
|
for await (let result of collector.collect()) {
|
|
assert.fail("Should not return any results");
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("class Counter", function() {
|
|
let counter;
|
|
beforeEach(function() {
|
|
counter = new prometheus.Counter("test", "Help", { register: false });
|
|
});
|
|
|
|
describe("constructor", function() {
|
|
it("should be initialized to 0", function() {
|
|
assert.equal(counter._values.get(""), 0);
|
|
});
|
|
});
|
|
describe(".inc()", function() {
|
|
it("should increment the counter by 1 by default", function() {
|
|
counter.inc();
|
|
assert.equal(counter._values.get(""), 1);
|
|
});
|
|
it("should increment the counter by specified value", function() {
|
|
counter.inc(3.2);
|
|
assert.equal(counter._values.get(""), 3.2);
|
|
});
|
|
it("should throw if value is negative", function() {
|
|
assert.throws(
|
|
() => counter.inc(-1),
|
|
new Error("Expected value to be a positive number")
|
|
);
|
|
});
|
|
it("should throw if value is negative infinity", function() {
|
|
assert.throws(
|
|
() => counter.inc(-Infinity),
|
|
new Error("Expected value to be a positive number")
|
|
);
|
|
});
|
|
it("should throw if value is NaN", function() {
|
|
assert.throws(
|
|
() => counter.inc(NaN),
|
|
new Error("Expected value to be a positive number")
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("class Gauge", function() {
|
|
let gauge;
|
|
beforeEach(function() {
|
|
gauge = new prometheus.Gauge("test", "Help", { register: false });
|
|
});
|
|
|
|
describe("constructor", function() {
|
|
it("should be initialized to 0", function() {
|
|
assert.equal(gauge._values.get(""), 0);
|
|
});
|
|
});
|
|
describe(".inc()", function() {
|
|
it("should increment the gauge by 1 by default", function() {
|
|
gauge.inc();
|
|
assert.equal(gauge._values.get(""), 1);
|
|
});
|
|
it("should increment the gauge by specified value", function() {
|
|
gauge.inc(3.2);
|
|
assert.equal(gauge._values.get(""), 3.2);
|
|
});
|
|
it("should decrement on negative value", function() {
|
|
gauge.inc(-2);
|
|
assert.equal(gauge._values.get(""), -2);
|
|
});
|
|
});
|
|
describe(".dec()", function() {
|
|
it("should decrement the gauge by 1 by default", function() {
|
|
gauge.dec();
|
|
assert.equal(gauge._values.get(""), -1);
|
|
});
|
|
it("should decrement the gauge by specified value", function() {
|
|
gauge.dec(3.2);
|
|
assert.equal(gauge._values.get(""), -3.2);
|
|
});
|
|
it("should increment on negative value", function() {
|
|
gauge.dec(-2);
|
|
assert.equal(gauge._values.get(""), 2);
|
|
});
|
|
});
|
|
describe(".set()", function() {
|
|
it("should set the gauge to the specified value", function() {
|
|
gauge.set(7);
|
|
assert.equal(gauge._values.get(""), 7);
|
|
});
|
|
});
|
|
describe(".setToCurrentTime()", function() {
|
|
it("should set the gauge to current unix time in seconds", function() {
|
|
gauge.setToCurrentTime();
|
|
assert(
|
|
Math.abs(gauge._values.get("") - Date.now() / 1000) < 1,
|
|
"value set is not close to the current unix time"
|
|
);
|
|
});
|
|
});
|
|
describe(".startTimer()", function() {
|
|
it("should set the gauge to a short duration", function() {
|
|
let setDuration = gauge.startTimer();
|
|
setDuration();
|
|
assert(
|
|
gauge._values.get("") > 0 && gauge._values.get("") < 1,
|
|
"value set is not between 0 and 1"
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("class Summary", function() {
|
|
let summary;
|
|
let labeledSummary;
|
|
beforeEach(function() {
|
|
summary = new prometheus.Summary(
|
|
"test", "Help", { register: false }
|
|
);
|
|
labeledSummary = new prometheus.Summary(
|
|
"test", "Help", { register: false, labels: ["a", "b"] }
|
|
);
|
|
});
|
|
|
|
describe("constructor", function() {
|
|
it("should have sum and count initialized to 0", function() {
|
|
assert.equal(summary.sum, 0);
|
|
assert.equal(summary.count, 0);
|
|
});
|
|
it("should throw on unrecoginzed option", function() {
|
|
assert.throws(
|
|
() => new prometheus.Summary("test", "help", { invalid: 1 }),
|
|
new Error("Unrecognized option 'invalid'")
|
|
);
|
|
});
|
|
});
|
|
|
|
describe(".observe()", function() {
|
|
it("should increment sum and count", function() {
|
|
summary.observe(2);
|
|
assert.equal(summary.sum, 2);
|
|
assert.equal(summary.count, 1);
|
|
|
|
summary.observe(4);
|
|
assert.equal(summary.sum, 6);
|
|
assert.equal(summary.count, 2);
|
|
});
|
|
});
|
|
|
|
describe(".startTimer()", function() {
|
|
it("should add observations when closure is called", function() {
|
|
let observeDuration = summary.startTimer();
|
|
assert.equal(summary.count, 0);
|
|
observeDuration();
|
|
assert.equal(summary.count, 1);
|
|
observeDuration();
|
|
assert.equal(summary.count, 2);
|
|
});
|
|
});
|
|
|
|
describe(".labels()", function() {
|
|
it("should initialize a value for the label set", function() {
|
|
labeledSummary.labels({ a: "1", b: "2" });
|
|
assert(labeledSummary._sumValues.has('a="1",b="2"'), "sum value not set");
|
|
assert(labeledSummary._countValues.has('a="1",b="2"'), "count value not set");
|
|
|
|
labeledSummary.labels("4", "5");
|
|
assert(labeledSummary._sumValues.has('a="4",b="5"'), "sum value not set");
|
|
assert(labeledSummary._countValues.has('a="4",b="5"'), "count value not set");
|
|
});
|
|
it("should return a child supporting the summary methods", function() {
|
|
let child = labeledSummary.labels({ a: "1", b: "2" });
|
|
void child.sum;
|
|
void child.count;
|
|
child.observe(2);
|
|
let observeDuration = child.startTimer();
|
|
observeDuration();
|
|
child = labeledSummary.labels("7", "8");
|
|
void child.sum;
|
|
void child.count;
|
|
child.observe(2);
|
|
observeDuration = child.startTimer();
|
|
observeDuration();
|
|
});
|
|
it("should return the same child for the same labels", function() {
|
|
let child = labeledSummary.labels("1", "2");
|
|
assert.equal(labeledSummary.labels("1", "2"), child);
|
|
assert.equal(labeledSummary.labels({ a: "1", b: "2" }), child);
|
|
});
|
|
});
|
|
describe(".remove()", function() {
|
|
it("should remove a label set created with .labels()", function() {
|
|
labeledSummary.labels("1", "2");
|
|
labeledSummary.remove("1", "2");
|
|
assert(!labeledSummary._sumValues.has('a="1",b="2"'), "sum value not removed");
|
|
assert(!labeledSummary._countValues.has('a="1",b="2"'), "count value not removed");
|
|
});
|
|
});
|
|
describe(".removeAll()", function() {
|
|
it("should remove a label set created with .labels()", function() {
|
|
labeledSummary.labels("1", "2");
|
|
labeledSummary.labels("1", "3");
|
|
labeledSummary.labels("4", "6");
|
|
labeledSummary.removeAll({ a: "1" });
|
|
assert.deepEqual(labeledSummary._sumValues, new Map([['a="4",b="6"', 0]]));
|
|
assert.deepEqual(labeledSummary._countValues, new Map([['a="4",b="6"', 0]]));
|
|
});
|
|
});
|
|
describe(".clear()", function() {
|
|
it("should remove all label sets created with .labels()", function() {
|
|
labeledSummary.labels("1", "2");
|
|
labeledSummary.labels("1", "3");
|
|
labeledSummary.labels("4", "6");
|
|
labeledSummary.clear();
|
|
assert(labeledSummary._sumValues.size === 0, "sums were not cleared");
|
|
assert(labeledSummary._countValues.size === 0, "counts were not cleared");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("class Histogram", function() {
|
|
let histogram;
|
|
let labeledHistogram;
|
|
beforeEach(function() {
|
|
histogram = new prometheus.Histogram(
|
|
"test", "Help", { register: false, buckets: [1, 2, 3, 4, Infinity] }
|
|
);
|
|
labeledHistogram = new prometheus.Histogram(
|
|
"test", "Help", { register: false, buckets: [1, Infinity], labels: ["a", "b"] }
|
|
);
|
|
});
|
|
|
|
describe("constructor", function() {
|
|
it("should have buckets, sum and count initialized to 0", function() {
|
|
assert.deepEqual(
|
|
histogram.buckets,
|
|
new Map([[1, 0], [2, 0], [3, 0], [4, 0], [Infinity, 0]])
|
|
);
|
|
assert.equal(histogram.sum, 0);
|
|
assert.equal(histogram.count, 0);
|
|
});
|
|
it("should provide a default for buckets", function() {
|
|
histogram = new prometheus.Histogram(
|
|
"test", "help", { register: false }
|
|
);
|
|
assert(histogram._buckets.length, "no default buckets were made");
|
|
});
|
|
it("should add inifinity bucket if not present", function() {
|
|
histogram = new prometheus.Histogram(
|
|
"test", "help", { buckets: [1], register: false }
|
|
);
|
|
assert.deepEqual(histogram._buckets, [1, Infinity]);
|
|
});
|
|
it("should not mutate bucket option passed to it", function() {
|
|
let buckets = [1];
|
|
histogram = new prometheus.Histogram(
|
|
"test", "help", { buckets, register: false }
|
|
);
|
|
assert.deepEqual(buckets, [1]);
|
|
});
|
|
it("should throw on unrecoginzed option", function() {
|
|
assert.throws(
|
|
() => new prometheus.Histogram("test", "help", { invalid: 1 }),
|
|
new Error("Unrecognized option 'invalid'")
|
|
);
|
|
});
|
|
it("should throw on using le as label", function() {
|
|
assert.throws(() => {
|
|
let test = new prometheus.Histogram(
|
|
"test", "Help", { register: false, labels: ["le", "test"] }
|
|
);
|
|
}, new Error("Reserved label 'le'"));
|
|
});
|
|
});
|
|
|
|
describe(".observe()", function() {
|
|
it("should increment the appropriate bucket", function() {
|
|
histogram.observe(2);
|
|
assert.deepEqual(
|
|
histogram.buckets,
|
|
new Map([[1, 0], [2, 1], [3, 1], [4, 1], [Infinity, 1]])
|
|
);
|
|
assert.equal(histogram.sum, 2);
|
|
assert.equal(histogram.count, 1);
|
|
|
|
histogram.observe(4);
|
|
assert.deepEqual(
|
|
histogram.buckets,
|
|
new Map([[1, 0], [2, 1], [3, 1], [4, 2], [Infinity, 2]])
|
|
);
|
|
assert.equal(histogram.sum, 6);
|
|
assert.equal(histogram.count, 2);
|
|
});
|
|
});
|
|
|
|
describe(".startTimer()", function() {
|
|
it("should add observations when closure is called", function() {
|
|
let observeDuration = histogram.startTimer();
|
|
assert.equal(histogram.count, 0);
|
|
observeDuration();
|
|
assert.equal(histogram.count, 1);
|
|
observeDuration();
|
|
assert.equal(histogram.count, 2);
|
|
});
|
|
});
|
|
|
|
describe("static .linear()", function() {
|
|
it("should give back the appropriate buckets", function() {
|
|
let linear = prometheus.Histogram.linear;
|
|
assert.deepEqual(linear(1, 2, 3), [1, 3, 5]);
|
|
assert.deepEqual(linear(-1, 4, 3), [-1, 3, 7]);
|
|
assert.deepEqual(linear(1, 1, 4), [1, 2, 3, 4]);
|
|
});
|
|
});
|
|
|
|
describe("static .exponential()", function() {
|
|
it("should give back the appropriate buckets", function() {
|
|
let exponential = prometheus.Histogram.exponential;
|
|
assert.deepEqual(exponential(1, 2, 3), [1, 2, 4]);
|
|
assert.deepEqual(exponential(2, 10, 3), [2, 20, 200]);
|
|
assert.deepEqual(exponential(1, 1.5, 4), [1, 1.5, 2.25, 3.375]);
|
|
});
|
|
});
|
|
|
|
describe(".labels()", function() {
|
|
it("should initialize a value for the label set", function() {
|
|
labeledHistogram.labels({ a: "1", b: "2" });
|
|
assert(labeledHistogram._bucketValues.has('a="1",b="2",le="1"'), "bucket value not set");
|
|
assert(labeledHistogram._bucketValues.has('a="1",b="2",le="+Inf"'), "bucket value not set");
|
|
assert(labeledHistogram._sumValues.has('a="1",b="2"'), "sum value not set");
|
|
assert(labeledHistogram._countValues.has('a="1",b="2"'), "count value not set");
|
|
|
|
labeledHistogram.labels("4", "5");
|
|
assert(labeledHistogram._bucketValues.has('a="4",b="5",le="1"'), "bucket value not set");
|
|
assert(labeledHistogram._bucketValues.has('a="4",b="5",le="+Inf"'), "bucket value not set");
|
|
assert(labeledHistogram._sumValues.has('a="4",b="5"'), "sum value not set");
|
|
assert(labeledHistogram._countValues.has('a="4",b="5"'), "count value not set");
|
|
});
|
|
it("should return a child supporting the histogram methods", function() {
|
|
let child = labeledHistogram.labels({ a: "1", b: "2" });
|
|
void child.buckets;
|
|
void child.sum;
|
|
void child.count;
|
|
child.observe(2);
|
|
let observeDuration = child.startTimer();
|
|
observeDuration();
|
|
child = labeledHistogram.labels("7", "8");
|
|
void child.buckets;
|
|
void child.sum;
|
|
void child.count;
|
|
child.observe(2);
|
|
observeDuration = child.startTimer();
|
|
observeDuration();
|
|
});
|
|
it("should return the same child for the same labels", function() {
|
|
let child = labeledHistogram.labels("1", "2");
|
|
assert.equal(labeledHistogram.labels("1", "2"), child);
|
|
assert.equal(labeledHistogram.labels({ a: "1", b: "2" }), child);
|
|
});
|
|
});
|
|
describe(".remove()", function() {
|
|
it("should remove a label set created with .labels()", function() {
|
|
labeledHistogram.labels("1", "2");
|
|
labeledHistogram.remove("1", "2");
|
|
assert(!labeledHistogram._bucketValues.has('a="1",b="2",le="1"'), "bucket value not removed");
|
|
assert(!labeledHistogram._bucketValues.has('a="1",b="2",le="+Inf"'), "bucket value not removed");
|
|
assert(!labeledHistogram._sumValues.has('a="1",b="2"'), "sum value not removed");
|
|
assert(!labeledHistogram._countValues.has('a="1",b="2"'), "count value not removed");
|
|
});
|
|
});
|
|
describe(".removeAll()", function() {
|
|
it("should remove a label set created with .labels()", function() {
|
|
labeledHistogram.labels("1", "2");
|
|
labeledHistogram.labels("1", "3");
|
|
labeledHistogram.labels("4", "6");
|
|
labeledHistogram.removeAll({ a: "1" });
|
|
assert.deepEqual(
|
|
labeledHistogram._bucketValues,
|
|
new Map([['a="4",b="6",le="1"', 0], ['a="4",b="6",le="+Inf"', 0]])
|
|
);
|
|
assert.deepEqual(labeledHistogram._sumValues, new Map([['a="4",b="6"', 0]]));
|
|
assert.deepEqual(labeledHistogram._countValues, new Map([['a="4",b="6"', 0]]));
|
|
});
|
|
});
|
|
describe(".clear()", function() {
|
|
it("should remove all label sets created with .labels()", function() {
|
|
labeledHistogram.labels("1", "2");
|
|
labeledHistogram.labels("1", "3");
|
|
labeledHistogram.labels("4", "6");
|
|
labeledHistogram.clear();
|
|
assert(labeledHistogram._bucketValues.size === 0, "buckets were not cleared");
|
|
assert(labeledHistogram._sumValues.size === 0, "sums were not cleared");
|
|
assert(labeledHistogram._countValues.size === 0, "counts were not cleared");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("class ValueCollector", function() {
|
|
describe(".get()", function() {
|
|
it("should return the current value", function() {
|
|
let gauge = new prometheus.Gauge("test", "Help", { register: false });
|
|
gauge.set(11);
|
|
assert.equal(gauge.get(), 11);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("class LabeledCollector", function() {
|
|
describe("constructor", function() {
|
|
it("should throw on invalid metric name", function() {
|
|
for (let name of ["", " ", "0a", "$a"]) {
|
|
assert.throws(
|
|
() => new prometheus.Gauge(
|
|
name, "Help", { register: false }
|
|
),
|
|
new Error(`Invalid name '${name}'`)
|
|
);
|
|
}
|
|
});
|
|
it("should throw on invalid label names", function() {
|
|
for (let name of ["", " ", "0a", "$a", "a:"]) {
|
|
assert.throws(
|
|
() => new prometheus.Gauge(
|
|
"test", "Help", { register: false, labels: [name] }
|
|
),
|
|
new Error(`Invalid label '${name}'`)
|
|
);
|
|
}
|
|
});
|
|
it("should throw on missing name", function() {
|
|
assert.throws(
|
|
() => new prometheus.Gauge(),
|
|
new Error("Expected name to be a string")
|
|
);
|
|
});
|
|
it("should throw on missing help", function() {
|
|
assert.throws(
|
|
() => new prometheus.Gauge("test"),
|
|
new Error("Expected help to be a string")
|
|
);
|
|
});
|
|
it("should throw on unrecoginzed option", function() {
|
|
assert.throws(
|
|
() => new prometheus.Gauge("test", "help", { invalid: 1 }),
|
|
new Error("Unrecognized option 'invalid'")
|
|
);
|
|
});
|
|
});
|
|
|
|
describe(".labels()", function() {
|
|
let gauge;
|
|
beforeEach(function() {
|
|
gauge = new prometheus.Gauge(
|
|
"test", "Help", { register: false, labels: ["a", "b"] }
|
|
);
|
|
});
|
|
it("should require all labels set at creation", function() {
|
|
assert.throws(
|
|
() => gauge.labels({ a: "1" }),
|
|
new Error("Missing label 'b'")
|
|
);
|
|
assert.throws(
|
|
() => gauge.labels({ b: "1" }),
|
|
new Error("Missing label 'a'")
|
|
);
|
|
assert.throws(
|
|
() => gauge.labels({ a: "1", b: "2", c: "3" }),
|
|
new Error("Extra label 'c'")
|
|
);
|
|
assert.throws(
|
|
() => gauge.labels("1"),
|
|
new Error("Missing label 'b'")
|
|
);
|
|
assert.throws(
|
|
() => gauge.labels("1", "2", "3"),
|
|
new Error("Extra positional label")
|
|
);
|
|
});
|
|
it("should throw if label value is not a string", function() {
|
|
assert.throws(
|
|
() => gauge.labels({ a: "1", b: 2 }),
|
|
new Error("Expected value for label 'b' to be a string")
|
|
);
|
|
assert.throws(
|
|
() => gauge.labels("1", 2),
|
|
new Error("Expected value for label 'b' to be a string")
|
|
);
|
|
});
|
|
it("should initialize a value for the label set", function() {
|
|
gauge.labels({ a: "1", b: "2" });
|
|
assert(gauge._values.has('a="1",b="2"'), "value was not set");
|
|
gauge.labels("4", "5");
|
|
assert(gauge._values.has('a="4",b="5"'), "value was not set");
|
|
});
|
|
it("should return a child supporting the usual methods", function() {
|
|
let child = gauge.labels({ a: "1", b: "2" });
|
|
child.get();
|
|
child.inc();
|
|
child.dec();
|
|
child.setToCurrentTime();
|
|
child = gauge.labels("7", "8");
|
|
child.get();
|
|
child.inc();
|
|
child.dec();
|
|
child.setToCurrentTime();
|
|
});
|
|
it("should return the same child for the same labels", function() {
|
|
let child = gauge.labels("1", "2");
|
|
assert.equal(gauge.labels("1", "2"), child);
|
|
assert.equal(gauge.labels({ a: "1", b: "2" }), child);
|
|
});
|
|
});
|
|
describe(".remove()", function() {
|
|
it("should remove a label set created with .labels()", function() {
|
|
let gauge = new prometheus.Gauge(
|
|
"test", "Help", { register: false, labels: ["a", "b"] }
|
|
);
|
|
gauge.labels({ a: "1", b: "2" });
|
|
gauge.remove({ a: "1", b: "2" });
|
|
assert(!gauge._values.has('a="1",b="2"'), "value was not removed");
|
|
});
|
|
it("should throw when removing the empty label set", function() {
|
|
let gauge = new prometheus.Gauge("test", "Help", { register: false });
|
|
assert.throws(
|
|
() => gauge.remove({}), new Error("labels cannot be empty")
|
|
);
|
|
});
|
|
});
|
|
describe(".removeAll()", function() {
|
|
it("should remove a label set created with .labels()", function() {
|
|
let gauge = new prometheus.Gauge(
|
|
"test", "Help", { register: false, labels: ["a", "b"] }
|
|
);
|
|
gauge.labels("1", "2");
|
|
gauge.labels("1", "3");
|
|
gauge.labels("4", "6");
|
|
gauge.removeAll({ a: "1" });
|
|
assert(gauge._values.size <= 1, "Matching labels remained");
|
|
assert(
|
|
gauge._values.has('a="4",b="6"'),
|
|
"Non-matching label was removed"
|
|
);
|
|
});
|
|
it("should throw when removing the empty label set", function() {
|
|
let gauge = new prometheus.Gauge("test", "Help", { register: false });
|
|
assert.throws(
|
|
() => gauge.removeAll({}), new Error("labels cannot be empty")
|
|
);
|
|
});
|
|
it("should handle special characters", function() {
|
|
let gauge = new prometheus.Gauge(
|
|
"test", "Help", { register: false, labels: ["a", "b"] }
|
|
);
|
|
|
|
let labelValue = "";
|
|
// Include every ASCII codepoint
|
|
for (let i = 0; i < 128; i++) {
|
|
labelValue += String.fromCodePoint(i);
|
|
}
|
|
// Include Unicode codepoints from the basic and supplementary planes.
|
|
labelValue += "©💣";
|
|
|
|
gauge.labels(labelValue, "2");
|
|
gauge.removeAll({ a: labelValue });
|
|
assert(gauge._values.size === 0, "Matching labels remained");
|
|
gauge.labels("1", labelValue);
|
|
gauge.removeAll({ a: "1" });
|
|
assert(gauge._values.size === 0, "Matching labels remained");
|
|
});
|
|
});
|
|
describe(".clear()", function() {
|
|
it("should remove all label sets created with .labels()", function() {
|
|
let gauge = new prometheus.Gauge(
|
|
"test", "Help", { register: false, labels: ["a", "b"] }
|
|
);
|
|
gauge.labels({ a: "1", b: "2" });
|
|
gauge.labels({ a: "3", b: "4" });
|
|
gauge.labels({ a: "5", b: "6" });
|
|
gauge.clear();
|
|
assert(gauge._values.size === 0, "values was not cleared");
|
|
});
|
|
it("should throw when used on an unlabled metric", function() {
|
|
let gauge = new prometheus.Gauge("test", "Help", { register: false });
|
|
assert.throws(
|
|
() => gauge.clear(), new Error("Cannot clear unlabeled metric")
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("defaultCollectors", function() {
|
|
describe("processStartTimeSeconds", function() {
|
|
it("should give a close point in time", async function() {
|
|
this.skip(); // XXX Doesn't work in Clusterio's shared testing env
|
|
let results = [];
|
|
let collector = prometheus.defaultCollectors.processStartTimeSeconds;
|
|
for await (let result of collector.collect()) {
|
|
results.push(result);
|
|
}
|
|
|
|
assert(
|
|
results.length === 1,
|
|
"collector did not give exactly one result"
|
|
);
|
|
|
|
let value = results[0].samples.get("").get("");
|
|
assert(
|
|
Math.abs(value - Date.now() / 1000) < 1,
|
|
"value set is not close to the current unix time"
|
|
);
|
|
});
|
|
});
|
|
describe("processCpuSecondsTotal", function() {
|
|
it("should give a low value", async function() {
|
|
this.skip(); // XXX Doesn't work in Clusterio's shared testing env
|
|
let results = [];
|
|
let collector = prometheus.defaultCollectors.processCpuSecondsTotal;
|
|
for await (let result of collector.collect()) {
|
|
results.push(result);
|
|
}
|
|
|
|
assert(
|
|
results.length === 1,
|
|
"collector did not give exactly one result"
|
|
);
|
|
|
|
let value = results[0].samples.get("").get("");
|
|
assert(
|
|
value < 5 && value > 0,
|
|
`process time is not between 0 and 5 seconds (${value})`
|
|
);
|
|
});
|
|
});
|
|
describe("processResidentMemoryBytes", function() {
|
|
it("should give a low value", async function() {
|
|
this.skip(); // XXX Doesn't work in Clusterio's shared testing env
|
|
let results = [];
|
|
let collector = prometheus.defaultCollectors.processResidentMemoryBytes;
|
|
for await (let result of collector.collect()) {
|
|
results.push(result);
|
|
}
|
|
|
|
assert(
|
|
results.length === 1,
|
|
"collector did not give exactly one result"
|
|
);
|
|
|
|
let value = results[0].samples.get("").get("");
|
|
assert(
|
|
value < 200e6 && value > 0,
|
|
`resident memory is not between 0 and 200 MB (${value})`
|
|
);
|
|
});
|
|
});
|
|
describe("processHeapBytes", function() {
|
|
it("should give a low value", async function() {
|
|
this.skip(); // XXX Doesn't work in Clusterio's shared testing env
|
|
let results = [];
|
|
let collector = prometheus.defaultCollectors.processHeapBytes;
|
|
for await (let result of collector.collect()) {
|
|
results.push(result);
|
|
}
|
|
|
|
assert(
|
|
results.length === 1,
|
|
"collector did not give exactly one result"
|
|
);
|
|
|
|
let value = results[0].samples.get("").get("");
|
|
assert(
|
|
value < 200e6 && value > 0,
|
|
`heap is not between 0 and 200 MB (${value})`
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("exposition()", function() {
|
|
it("should format simple metrics", async function() {
|
|
let registry = new prometheus.CollectorRegistry();
|
|
let counter = new prometheus.Counter("a", "Help", { register: false });
|
|
registry.register(counter);
|
|
let gauge = new prometheus.Gauge("b", "Help", { register: false });
|
|
registry.register(gauge);
|
|
let exposition = await prometheus.exposition(registry.collect());
|
|
assert.equal(
|
|
exposition,
|
|
"# HELP a Help\n" +
|
|
"# TYPE a counter\n" +
|
|
"a 0\n" +
|
|
"\n" +
|
|
"# HELP b Help\n" +
|
|
"# TYPE b gauge\n" +
|
|
"b 0\n"
|
|
);
|
|
});
|
|
it("should format labeled metrics", async function() {
|
|
let registry = new prometheus.CollectorRegistry();
|
|
let gauge = new prometheus.Gauge(
|
|
"test", "Help", { register: false, labels: ["a", "b"] }
|
|
);
|
|
gauge.labels({ a: "1", b: "2" });
|
|
gauge.labels({ b: "y", a: "x" });
|
|
registry.register(gauge);
|
|
let exposition = await prometheus.exposition(registry.collect());
|
|
assert.equal(
|
|
exposition,
|
|
"# HELP test Help\n" +
|
|
"# TYPE test gauge\n" +
|
|
'test{a="1",b="2"} 0\n' +
|
|
'test{a="x",b="y"} 0\n'
|
|
);
|
|
});
|
|
it("should format histogram metrics", async function() {
|
|
let registry = new prometheus.CollectorRegistry();
|
|
let histogram = new prometheus.Histogram(
|
|
"test", "Help", { register: false, buckets: [1, 2, 3, 4] }
|
|
);
|
|
for (let value of [2, 2, 5, 2]) {
|
|
histogram.observe(value);
|
|
}
|
|
registry.register(histogram);
|
|
let exposition = await prometheus.exposition(registry.collect());
|
|
assert.equal(
|
|
exposition,
|
|
"# HELP test Help\n" +
|
|
"# TYPE test histogram\n" +
|
|
'test_bucket{le="1"} 0\n' +
|
|
'test_bucket{le="2"} 3\n' +
|
|
'test_bucket{le="3"} 3\n' +
|
|
'test_bucket{le="4"} 3\n' +
|
|
'test_bucket{le="+Inf"} 4\n' +
|
|
"test_sum 11\n" +
|
|
"test_count 4\n"
|
|
);
|
|
});
|
|
it("should format histogram metrics with labels", async function() {
|
|
let registry = new prometheus.CollectorRegistry();
|
|
let histogram = new prometheus.Histogram(
|
|
"test", "Help", { register: false, buckets: [1], labels: ["l"] }
|
|
);
|
|
for (let [value, label] of [[1, "a"], [2, "b"], [3, "b"]]) {
|
|
histogram.labels(label).observe(value);
|
|
}
|
|
histogram.labels("c");
|
|
|
|
registry.register(histogram);
|
|
let exposition = await prometheus.exposition(registry.collect());
|
|
assert.equal(
|
|
exposition,
|
|
"# HELP test Help\n" +
|
|
"# TYPE test histogram\n" +
|
|
'test_bucket{l="a",le="1"} 1\n' +
|
|
'test_bucket{l="a",le="+Inf"} 1\n' +
|
|
'test_bucket{l="b",le="1"} 0\n' +
|
|
'test_bucket{l="b",le="+Inf"} 2\n' +
|
|
'test_bucket{l="c",le="1"} 0\n' +
|
|
'test_bucket{l="c",le="+Inf"} 0\n' +
|
|
'test_sum{l="a"} 1\n' +
|
|
'test_sum{l="b"} 5\n' +
|
|
'test_sum{l="c"} 0\n' +
|
|
'test_count{l="a"} 1\n' +
|
|
'test_count{l="b"} 2\n' +
|
|
'test_count{l="c"} 0\n'
|
|
);
|
|
});
|
|
it("should format Infinity and NaN", async function() {
|
|
let registry = new prometheus.CollectorRegistry();
|
|
let gauge = new prometheus.Gauge(
|
|
"test", "Help", { register: false, labels: ["n"] }
|
|
);
|
|
gauge.labels({ n: "1" }).set(Infinity);
|
|
gauge.labels({ n: "2" }).set(-Infinity);
|
|
gauge.labels({ n: "3" }).set(NaN);
|
|
registry.register(gauge);
|
|
let exposition = await prometheus.exposition(registry.collect());
|
|
assert.equal(
|
|
exposition,
|
|
"# HELP test Help\n" +
|
|
"# TYPE test gauge\n" +
|
|
'test{n="1"} +Inf\n' +
|
|
'test{n="2"} -Inf\n' +
|
|
'test{n="3"} NaN\n'
|
|
);
|
|
});
|
|
});
|
|
|
|
let serializedResult = {
|
|
metric: { type: "type", name: "name", help: "Help", labels: ["a", "b"] },
|
|
samples: [["", [['a="1",b="2"', 1], ['a="3",b="4"', 5]]]],
|
|
};
|
|
let deserializedResult = {
|
|
metric: new prometheus.Metric("type", "name", "Help", ["a", "b"]),
|
|
samples: new Map([["", new Map([['a="1",b="2"', 1], ['a="3",b="4"', 5]])]]),
|
|
};
|
|
describe("serializeResult()", function() {
|
|
it("should convert real results into the reference serialization", async function() {
|
|
let counter = new prometheus.Counter("a_counter", "Counter", { register: false });
|
|
let gauge = new prometheus.Gauge("a_gauge", "Gauge", { register: false, labels: ["a", "b"] });
|
|
let registry = new prometheus.CollectorRegistry();
|
|
registry.register(counter);
|
|
registry.register(gauge);
|
|
|
|
counter.inc(3);
|
|
gauge.labels("1", "2").set(1);
|
|
gauge.labels("3", "4").set(5);
|
|
|
|
let results = [];
|
|
for await (let result of registry.collect()) {
|
|
results.push(result);
|
|
}
|
|
|
|
let referenceSerialization = [
|
|
{
|
|
metric: { type: "counter", name: "a_counter", help: "Counter", labels: [] },
|
|
samples: [["", [["", 3]]]],
|
|
},
|
|
{
|
|
metric: { type: "gauge", name: "a_gauge", help: "Gauge", labels: ["a", "b"] },
|
|
samples: [["", [['a="1",b="2"', 1], ['a="3",b="4"', 5]]]],
|
|
},
|
|
];
|
|
|
|
assert.deepEqual(
|
|
results.map(result => prometheus.serializeResult(result)),
|
|
referenceSerialization
|
|
);
|
|
});
|
|
it("should support adding labels", function() {
|
|
assert.deepEqual(
|
|
prometheus.serializeResult({
|
|
metric: new prometheus.Metric("type", "name", "Help"),
|
|
samples: new Map([["name", new Map([["", 2]])]]),
|
|
}, { addLabels: { c: "8" }}),
|
|
{
|
|
metric: { type: "type", name: "name", help: "Help", labels: ["c"] },
|
|
samples: [["name", [['c="8"', 2]]]],
|
|
}
|
|
);
|
|
assert.deepEqual(
|
|
prometheus.serializeResult({
|
|
metric: new prometheus.Metric("type", "name", "Help", ["a"]),
|
|
samples: new Map([["", new Map([['a="4"', 3]])]]),
|
|
}, { addLabels: { c: "8" }}),
|
|
{
|
|
metric: { type: "type", name: "name", help: "Help", labels: ["a", "c"] },
|
|
samples: [["", [['a="4",c="8"', 3]]]],
|
|
}
|
|
);
|
|
});
|
|
it("should support redefining name and help", function() {
|
|
assert.deepEqual(
|
|
prometheus.serializeResult({
|
|
metric: new prometheus.Metric("type", "name", "Help"),
|
|
samples: new Map([["", new Map([["", 2]])]]),
|
|
}, { metricName: "new" }),
|
|
{
|
|
metric: { type: "type", name: "new", help: "Help", labels: [] },
|
|
samples: [["", [["", 2]]]],
|
|
}
|
|
);
|
|
assert.deepEqual(
|
|
prometheus.serializeResult({
|
|
metric: new prometheus.Metric("type", "name", "Help"),
|
|
samples: new Map([["", new Map([["", 2]])]]),
|
|
}, { metricHelp: "old" }),
|
|
{
|
|
metric: { type: "type", name: "name", help: "old", labels: [] },
|
|
samples: [["", [["", 2]]]],
|
|
}
|
|
);
|
|
});
|
|
it("should throw on unrecoginzed option", function() {
|
|
assert.throws(
|
|
() => prometheus.serializeResult({
|
|
metric: new prometheus.Metric("type", "name", "Help"),
|
|
samples: new Map([["", new Map([["", 3]])]]),
|
|
}, { invalid: true }),
|
|
new Error("Unrecognized option 'invalid'")
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("deserializeResult()", function() {
|
|
it("should convert a plain object into a result", function() {
|
|
assert.deepEqual(
|
|
prometheus.deserializeResult(serializedResult),
|
|
deserializedResult
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("integration", function() {
|
|
it("should round trip serialize histogram results", async function() {
|
|
let registry = new prometheus.CollectorRegistry();
|
|
let histogram = new prometheus.Histogram(
|
|
"test", "Help", { register: false, buckets: [1], labels: ["l"] }
|
|
);
|
|
for (let [value, label] of [[1, "a"], [2, "b"], [3, "b"]]) {
|
|
histogram.labels(label).observe(value);
|
|
}
|
|
histogram.labels("c");
|
|
|
|
registry.register(histogram);
|
|
let results = [];
|
|
for await (let result of registry.collect()) {
|
|
results.push(result);
|
|
}
|
|
let exposition = await prometheus.exposition(results);
|
|
|
|
let serialized = results.map(res => prometheus.serializeResult(res));
|
|
let jsonRoundtrip = JSON.parse(JSON.stringify(serialized));
|
|
let deserialized = jsonRoundtrip.map(res => prometheus.deserializeResult(res));
|
|
assert.deepEqual(deserialized, results);
|
|
let roundTripExposition = await prometheus.exposition(deserialized);
|
|
|
|
assert.equal(roundTripExposition, exposition);
|
|
});
|
|
});
|
|
|
|
describe("post test checks", function() {
|
|
it("should not have any collectors in the default registry", function() {
|
|
this.skip(); // XXX Doesn't work in Clusterio's shared testing env
|
|
assert(
|
|
prometheus.defaultRegistry.collectors.length === 0,
|
|
"Collectors left over by test code"
|
|
);
|
|
});
|
|
});
|
|
});
|