@oneuptime/common 11.5.10 → 11.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/AnalyticsModels/NetworkFlow.ts +12 -1
- package/Server/API/StatusPageAPI.ts +14 -1
- package/Server/Services/IncidentService.ts +62 -67
- package/Server/Services/MonitorGroupService.ts +15 -2
- package/Server/Services/MonitorService.ts +91 -3
- package/Server/Services/MonitorStatusTimelineService.ts +285 -100
- package/Server/Services/StatusPageService.ts +59 -34
- package/Server/Utils/Monitor/MonitorResource.ts +43 -14
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +32 -7
- package/Server/Utils/Monitor/MonitorStatusTimelineReconciler.ts +362 -0
- package/Tests/Server/Services/MonitorStatusTimelineService.test.ts +235 -0
- package/Tests/Server/Utils/Monitor/MonitorStatusTimelineReconciler.test.ts +555 -0
- package/Tests/Types/Database/CompareOperatorWireSerialization.test.ts +216 -0
- package/Tests/Types/Database/GreaterThan.test.ts +5 -1
- package/Tests/Types/Database/GreaterThanOrEqual.test.ts +5 -1
- package/Tests/Types/Database/GreaterThanOrNull.test.ts +24 -1
- package/Tests/Types/Database/LessThan.test.ts +5 -1
- package/Tests/Types/Database/LessThanOrEqual.test.ts +24 -1
- package/Tests/Types/Database/LessThanOrNull.test.ts +5 -1
- package/Tests/Utils/Uptime/UptimeUtil.test.ts +726 -0
- package/Types/BaseDatabase/GreaterThan.ts +8 -1
- package/Types/BaseDatabase/GreaterThanOrEqual.ts +8 -1
- package/Types/BaseDatabase/GreaterThanOrNull.ts +9 -1
- package/Types/BaseDatabase/LessThan.ts +8 -1
- package/Types/BaseDatabase/LessThanOrEqual.ts +9 -1
- package/Types/BaseDatabase/LessThanOrNull.ts +8 -1
- package/Utils/StatusPage/ResourceUptime.ts +11 -1
- package/Utils/Uptime/UptimeUtil.ts +142 -36
- package/build/dist/Models/AnalyticsModels/NetworkFlow.js +12 -1
- package/build/dist/Models/AnalyticsModels/NetworkFlow.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +12 -2
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +47 -56
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/MonitorGroupService.js +15 -2
- package/build/dist/Server/Services/MonitorGroupService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +66 -3
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +244 -102
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +57 -33
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +34 -12
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +25 -8
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimelineReconciler.js +268 -0
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimelineReconciler.js.map +1 -0
- package/build/dist/Types/BaseDatabase/GreaterThan.js +8 -1
- package/build/dist/Types/BaseDatabase/GreaterThan.js.map +1 -1
- package/build/dist/Types/BaseDatabase/GreaterThanOrEqual.js +8 -1
- package/build/dist/Types/BaseDatabase/GreaterThanOrEqual.js.map +1 -1
- package/build/dist/Types/BaseDatabase/GreaterThanOrNull.js +9 -1
- package/build/dist/Types/BaseDatabase/GreaterThanOrNull.js.map +1 -1
- package/build/dist/Types/BaseDatabase/LessThan.js +8 -1
- package/build/dist/Types/BaseDatabase/LessThan.js.map +1 -1
- package/build/dist/Types/BaseDatabase/LessThanOrEqual.js +9 -1
- package/build/dist/Types/BaseDatabase/LessThanOrEqual.js.map +1 -1
- package/build/dist/Types/BaseDatabase/LessThanOrNull.js +8 -1
- package/build/dist/Types/BaseDatabase/LessThanOrNull.js.map +1 -1
- package/build/dist/Utils/StatusPage/ResourceUptime.js +4 -1
- package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
- package/build/dist/Utils/Uptime/UptimeUtil.js +86 -33
- package/build/dist/Utils/Uptime/UptimeUtil.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import CompareBase, { CompareType } from "../../../Types/Database/CompareBase";
|
|
2
|
+
import GreaterThan from "../../../Types/BaseDatabase/GreaterThan";
|
|
3
|
+
import GreaterThanOrEqual from "../../../Types/BaseDatabase/GreaterThanOrEqual";
|
|
4
|
+
import GreaterThanOrNull from "../../../Types/BaseDatabase/GreaterThanOrNull";
|
|
5
|
+
import InBetween from "../../../Types/BaseDatabase/InBetween";
|
|
6
|
+
import LessThan from "../../../Types/BaseDatabase/LessThan";
|
|
7
|
+
import LessThanOrEqual from "../../../Types/BaseDatabase/LessThanOrEqual";
|
|
8
|
+
import LessThanOrNull from "../../../Types/BaseDatabase/LessThanOrNull";
|
|
9
|
+
import JSONFunctions from "../../../Types/JSONFunctions";
|
|
10
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
11
|
+
import { describe, expect, it } from "@jest/globals";
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* Pins the WIRE CONTRACT of the comparison query operators end to end:
|
|
15
|
+
*
|
|
16
|
+
* browser: JSONFunctions.serialize(query) -> operator.toJSON()
|
|
17
|
+
* HTTP: JSON.stringify / JSON.parse (a raw Date becomes its full ISO string)
|
|
18
|
+
* server: JSONFunctions.deserialize -> Dictionary[_type].fromJSON
|
|
19
|
+
* SQL bind: operator.toString() -> QueryHelper.<op>(value)
|
|
20
|
+
*
|
|
21
|
+
* The bug this file guards against: toJSON used to serialize Date values via
|
|
22
|
+
* toString(), which collapses them to a DATE-ONLY string in the BROWSER'S LOCAL
|
|
23
|
+
* TIMEZONE (OneUptimeDate.asDateForDatabaseQuery). A bound of "now" therefore
|
|
24
|
+
* arrived at the server as midnight of the viewer's calendar date, and every
|
|
25
|
+
* row from later that day was silently excluded from the query - e.g. the
|
|
26
|
+
* Monitor View page dropped a monitor's current open status row whenever the
|
|
27
|
+
* status had changed the same day. Serializing the RAW value (exactly what
|
|
28
|
+
* InBetween always did) keeps the full timestamp across the wire in every
|
|
29
|
+
* timezone.
|
|
30
|
+
*
|
|
31
|
+
* The legacy date-only wire format must STAY accepted: an old client (or a
|
|
32
|
+
* saved query serialized before this change) sends value: "YYYY-MM-DD", and
|
|
33
|
+
* the server must keep treating it as an opaque string bound.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const FULL_ISO_TIMESTAMP: string = "2026-07-21T14:35:12.345Z";
|
|
37
|
+
const LEGACY_DATE_ONLY: string = "2026-07-21";
|
|
38
|
+
|
|
39
|
+
type OperatorFactory = (value: CompareType) => CompareBase<CompareType>;
|
|
40
|
+
|
|
41
|
+
type OperatorCase = {
|
|
42
|
+
name: string;
|
|
43
|
+
type: unknown;
|
|
44
|
+
create: OperatorFactory;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const OPERATOR_CASES: Array<OperatorCase> = [
|
|
48
|
+
{
|
|
49
|
+
name: "LessThan",
|
|
50
|
+
type: LessThan,
|
|
51
|
+
create: (value: CompareType) => {
|
|
52
|
+
return new LessThan(value);
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "LessThanOrEqual",
|
|
57
|
+
type: LessThanOrEqual,
|
|
58
|
+
create: (value: CompareType) => {
|
|
59
|
+
return new LessThanOrEqual(value);
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "LessThanOrNull",
|
|
64
|
+
type: LessThanOrNull,
|
|
65
|
+
create: (value: CompareType) => {
|
|
66
|
+
return new LessThanOrNull(value);
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "GreaterThan",
|
|
71
|
+
type: GreaterThan,
|
|
72
|
+
create: (value: CompareType) => {
|
|
73
|
+
return new GreaterThan(value);
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "GreaterThanOrEqual",
|
|
78
|
+
type: GreaterThanOrEqual,
|
|
79
|
+
create: (value: CompareType) => {
|
|
80
|
+
return new GreaterThanOrEqual(value);
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "GreaterThanOrNull",
|
|
85
|
+
type: GreaterThanOrNull,
|
|
86
|
+
create: (value: CompareType) => {
|
|
87
|
+
return new GreaterThanOrNull(value);
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
type SimulateHttpHopFunction = (query: JSONObject) => JSONObject;
|
|
93
|
+
|
|
94
|
+
/*
|
|
95
|
+
* What actually happens between ModelAPI.getList and BaseAPI: the serialized
|
|
96
|
+
* query is JSON.stringify'd into the request body and JSON.parse'd on the
|
|
97
|
+
* server. This is the step that turns a raw Date into its full ISO string.
|
|
98
|
+
*/
|
|
99
|
+
const simulateHttpHop: SimulateHttpHopFunction = (
|
|
100
|
+
query: JSONObject,
|
|
101
|
+
): JSONObject => {
|
|
102
|
+
return JSON.parse(JSON.stringify(JSONFunctions.serialize(query)));
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
for (const operatorCase of OPERATOR_CASES) {
|
|
106
|
+
describe(`${operatorCase.name} wire serialization`, () => {
|
|
107
|
+
it("sends a Date bound as its full ISO timestamp, not a local date-only string", () => {
|
|
108
|
+
const query: JSONObject = {
|
|
109
|
+
startsAt: operatorCase.create(new Date(FULL_ISO_TIMESTAMP)),
|
|
110
|
+
} as unknown as JSONObject;
|
|
111
|
+
|
|
112
|
+
const wire: JSONObject = simulateHttpHop(query);
|
|
113
|
+
|
|
114
|
+
expect((wire["startsAt"] as JSONObject)["_type"]).toBe(operatorCase.name);
|
|
115
|
+
/*
|
|
116
|
+
* The critical assertion: the full timestamp survives. Under the old
|
|
117
|
+
* toString()-based toJSON this was "2026-07-21" (or "2026-07-22" east
|
|
118
|
+
* of UTC) regardless of the time component.
|
|
119
|
+
*/
|
|
120
|
+
expect((wire["startsAt"] as JSONObject)["value"]).toBe(
|
|
121
|
+
FULL_ISO_TIMESTAMP,
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("deserializes on the server to the operator with the full-precision bound", () => {
|
|
126
|
+
const query: JSONObject = {
|
|
127
|
+
startsAt: operatorCase.create(new Date(FULL_ISO_TIMESTAMP)),
|
|
128
|
+
} as unknown as JSONObject;
|
|
129
|
+
|
|
130
|
+
const deserialized: JSONObject = JSONFunctions.deserialize(
|
|
131
|
+
simulateHttpHop(query),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
expect(deserialized["startsAt"]).toBeInstanceOf(operatorCase.type);
|
|
135
|
+
|
|
136
|
+
/*
|
|
137
|
+
* toString() is exactly what QueryUtil passes into QueryHelper as the
|
|
138
|
+
* SQL bind value, so this string IS the query bound. It must be the
|
|
139
|
+
* full ISO timestamp, passed through untouched.
|
|
140
|
+
*/
|
|
141
|
+
const operator: CompareBase<CompareType> = deserialized[
|
|
142
|
+
"startsAt"
|
|
143
|
+
] as unknown as CompareBase<CompareType>;
|
|
144
|
+
|
|
145
|
+
expect(operator.toString()).toBe(FULL_ISO_TIMESTAMP);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("still accepts the legacy date-only wire format as an opaque string", () => {
|
|
149
|
+
const legacyWire: JSONObject = {
|
|
150
|
+
startsAt: {
|
|
151
|
+
_type: operatorCase.name,
|
|
152
|
+
value: LEGACY_DATE_ONLY,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const deserialized: JSONObject = JSONFunctions.deserialize(legacyWire);
|
|
157
|
+
|
|
158
|
+
expect(deserialized["startsAt"]).toBeInstanceOf(operatorCase.type);
|
|
159
|
+
|
|
160
|
+
const operator: CompareBase<CompareType> = deserialized[
|
|
161
|
+
"startsAt"
|
|
162
|
+
] as unknown as CompareBase<CompareType>;
|
|
163
|
+
|
|
164
|
+
expect(operator.toString()).toBe(LEGACY_DATE_ONLY);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("round-trips a number bound unchanged", () => {
|
|
168
|
+
const query: JSONObject = {
|
|
169
|
+
priority: operatorCase.create(42),
|
|
170
|
+
} as unknown as JSONObject;
|
|
171
|
+
|
|
172
|
+
const wire: JSONObject = simulateHttpHop(query);
|
|
173
|
+
|
|
174
|
+
expect((wire["priority"] as JSONObject)["value"]).toBe(42);
|
|
175
|
+
|
|
176
|
+
const deserialized: JSONObject = JSONFunctions.deserialize(wire);
|
|
177
|
+
|
|
178
|
+
const operator: CompareBase<CompareType> = deserialized[
|
|
179
|
+
"priority"
|
|
180
|
+
] as unknown as CompareBase<CompareType>;
|
|
181
|
+
|
|
182
|
+
expect(operator.toString()).toBe("42");
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
describe("InBetween wire serialization (the reference behavior)", () => {
|
|
188
|
+
it("has always sent full ISO timestamps for both bounds - the operators above now match it", () => {
|
|
189
|
+
const startBound: string = "2026-04-22T00:30:00.000Z";
|
|
190
|
+
|
|
191
|
+
const query: JSONObject = {
|
|
192
|
+
createdAt: new InBetween(
|
|
193
|
+
new Date(startBound),
|
|
194
|
+
new Date(FULL_ISO_TIMESTAMP),
|
|
195
|
+
),
|
|
196
|
+
} as unknown as JSONObject;
|
|
197
|
+
|
|
198
|
+
const wire: JSONObject = simulateHttpHop(query);
|
|
199
|
+
|
|
200
|
+
expect((wire["createdAt"] as JSONObject)["startValue"]).toBe(startBound);
|
|
201
|
+
expect((wire["createdAt"] as JSONObject)["endValue"]).toBe(
|
|
202
|
+
FULL_ISO_TIMESTAMP,
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const deserialized: JSONObject = JSONFunctions.deserialize(wire);
|
|
206
|
+
|
|
207
|
+
expect(deserialized["createdAt"]).toBeInstanceOf(InBetween);
|
|
208
|
+
|
|
209
|
+
const operator: InBetween<CompareType> = deserialized[
|
|
210
|
+
"createdAt"
|
|
211
|
+
] as unknown as InBetween<CompareType>;
|
|
212
|
+
|
|
213
|
+
expect(operator.toStartValueString()).toBe(startBound);
|
|
214
|
+
expect(operator.toEndValueString()).toBe(FULL_ISO_TIMESTAMP);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
@@ -25,7 +25,11 @@ describe("GreaterThan", () => {
|
|
|
25
25
|
const obj: GreaterThan<number> = new GreaterThan<number>(42);
|
|
26
26
|
const expectedJSON: JSONObject = {
|
|
27
27
|
_type: "GreaterThan",
|
|
28
|
-
|
|
28
|
+
/*
|
|
29
|
+
* toJSON carries the RAW value (like InBetween), not a stringified one -
|
|
30
|
+
* see the comment on toJSON. For numbers that means the number itself.
|
|
31
|
+
*/
|
|
32
|
+
value: 42,
|
|
29
33
|
};
|
|
30
34
|
expect(obj.toJSON()).toEqual(expectedJSON);
|
|
31
35
|
});
|
|
@@ -27,7 +27,11 @@ describe("GreaterThanOrEqual", () => {
|
|
|
27
27
|
const obj: GreaterThanOrEqual<number> = new GreaterThanOrEqual<number>(42);
|
|
28
28
|
const expectedJSON: JSONObject = {
|
|
29
29
|
_type: "GreaterThanOrEqual",
|
|
30
|
-
|
|
30
|
+
/*
|
|
31
|
+
* toJSON carries the RAW value (like InBetween), not a stringified one -
|
|
32
|
+
* see the comment on toJSON. For numbers that means the number itself.
|
|
33
|
+
*/
|
|
34
|
+
value: 42,
|
|
31
35
|
};
|
|
32
36
|
expect(obj.toJSON()).toEqual(expectedJSON);
|
|
33
37
|
});
|
|
@@ -23,13 +23,36 @@ describe("GreaterThanOrNull", () => {
|
|
|
23
23
|
|
|
24
24
|
it("should generate the correct JSON representation using toJSON", () => {
|
|
25
25
|
const obj: GreaterThanOrNull<number> = new GreaterThanOrNull<number>(42);
|
|
26
|
+
/*
|
|
27
|
+
* toJSON carries the RAW value (like InBetween), not a stringified one -
|
|
28
|
+
* see the comment on toJSON. For numbers that means the number itself.
|
|
29
|
+
*/
|
|
26
30
|
const expectedJSON: JSONObject = {
|
|
27
31
|
_type: "GreaterThanOrNull",
|
|
28
|
-
value:
|
|
32
|
+
value: 42,
|
|
29
33
|
};
|
|
30
34
|
expect(obj.toJSON()).toEqual(expectedJSON);
|
|
31
35
|
});
|
|
32
36
|
|
|
37
|
+
it("keeps the full timestamp when serializing a Date value", () => {
|
|
38
|
+
const value: Date = new Date("2026-07-21T14:35:12.345Z");
|
|
39
|
+
const obj: GreaterThanOrNull<Date> = new GreaterThanOrNull<Date>(value);
|
|
40
|
+
|
|
41
|
+
/*
|
|
42
|
+
* The raw Date is serialized, so JSON.stringify emits the full ISO
|
|
43
|
+
* timestamp. The old toString()-based toJSON collapsed it to a
|
|
44
|
+
* local-timezone date-only string, which shifted query bounds sent from
|
|
45
|
+
* the browser by up to a day and silently dropped same-day rows.
|
|
46
|
+
*/
|
|
47
|
+
expect(obj.toJSON()).toEqual({
|
|
48
|
+
_type: "GreaterThanOrNull",
|
|
49
|
+
value: value,
|
|
50
|
+
});
|
|
51
|
+
expect(JSON.parse(JSON.stringify(obj.toJSON()))["value"]).toBe(
|
|
52
|
+
"2026-07-21T14:35:12.345Z",
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
33
56
|
it("should create a GreaterThanOrNull object from valid JSON input", () => {
|
|
34
57
|
const jsonInput: JSONObject = {
|
|
35
58
|
_type: "GreaterThanOrNull",
|
|
@@ -25,7 +25,11 @@ describe("LessThan", () => {
|
|
|
25
25
|
const obj: LessThan<number> = new LessThan<number>(42);
|
|
26
26
|
const expectedJSON: JSONObject = {
|
|
27
27
|
_type: "LessThan",
|
|
28
|
-
|
|
28
|
+
/*
|
|
29
|
+
* toJSON carries the RAW value (like InBetween), not a stringified one -
|
|
30
|
+
* see the comment on toJSON. For numbers that means the number itself.
|
|
31
|
+
*/
|
|
32
|
+
value: 42,
|
|
29
33
|
};
|
|
30
34
|
expect(obj.toJSON()).toEqual(expectedJSON);
|
|
31
35
|
});
|
|
@@ -23,13 +23,36 @@ describe("LessThanOrEqual", () => {
|
|
|
23
23
|
|
|
24
24
|
it("should generate the correct JSON representation using toJSON", () => {
|
|
25
25
|
const obj: LessThanOrEqual<number> = new LessThanOrEqual<number>(42);
|
|
26
|
+
/*
|
|
27
|
+
* toJSON carries the RAW value (like InBetween), not a stringified one -
|
|
28
|
+
* see the comment on toJSON. For numbers that means the number itself.
|
|
29
|
+
*/
|
|
26
30
|
const expectedJSON: JSONObject = {
|
|
27
31
|
_type: "LessThanOrEqual",
|
|
28
|
-
value:
|
|
32
|
+
value: 42,
|
|
29
33
|
};
|
|
30
34
|
expect(obj.toJSON()).toEqual(expectedJSON);
|
|
31
35
|
});
|
|
32
36
|
|
|
37
|
+
it("keeps the full timestamp when serializing a Date value", () => {
|
|
38
|
+
const value: Date = new Date("2026-07-21T14:35:12.345Z");
|
|
39
|
+
const obj: LessThanOrEqual<Date> = new LessThanOrEqual<Date>(value);
|
|
40
|
+
|
|
41
|
+
/*
|
|
42
|
+
* The raw Date is serialized, so JSON.stringify emits the full ISO
|
|
43
|
+
* timestamp. The old toString()-based toJSON collapsed it to a
|
|
44
|
+
* local-timezone date-only string, which shifted query bounds sent from
|
|
45
|
+
* the browser by up to a day and silently dropped same-day rows.
|
|
46
|
+
*/
|
|
47
|
+
expect(obj.toJSON()).toEqual({
|
|
48
|
+
_type: "LessThanOrEqual",
|
|
49
|
+
value: value,
|
|
50
|
+
});
|
|
51
|
+
expect(JSON.parse(JSON.stringify(obj.toJSON()))["value"]).toBe(
|
|
52
|
+
"2026-07-21T14:35:12.345Z",
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
33
56
|
it("should create a LessThanOrEqual object from valid JSON input", () => {
|
|
34
57
|
const jsonInput: JSONObject = {
|
|
35
58
|
_type: "LessThanOrEqual",
|
|
@@ -25,7 +25,11 @@ describe("LessThanOrNull", () => {
|
|
|
25
25
|
const obj: LessThanOrNull<number> = new LessThanOrNull<number>(42);
|
|
26
26
|
const expectedJSON: JSONObject = {
|
|
27
27
|
_type: "LessThanOrNull",
|
|
28
|
-
|
|
28
|
+
/*
|
|
29
|
+
* toJSON carries the RAW value (like InBetween), not a stringified one -
|
|
30
|
+
* see the comment on toJSON. For numbers that means the number itself.
|
|
31
|
+
*/
|
|
32
|
+
value: 42,
|
|
29
33
|
};
|
|
30
34
|
expect(obj.toJSON()).toEqual(expectedJSON);
|
|
31
35
|
});
|