Splits the config options for the timings of a delayed leave event into two sets: one for when delegation is available (as you can relax the timings and get more stable calls this way), and another for when it's unavailable (as we must continue to gracefully downgrade even after Matrix 2.0 is fully rolled out). This works by bluntly hitting the delegation endpoints without auth before joining to check for a 404.
126 lines
3.7 KiB
TypeScript
126 lines
3.7 KiB
TypeScript
/*
|
|
Copyright 2025 New Vector Ltd.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE in the repository root for full details.
|
|
*/
|
|
|
|
import { expect, test } from "vitest";
|
|
import { type Observable, of, Subject, switchMap } from "rxjs";
|
|
|
|
import { withTestScheduler } from "./test";
|
|
import { or$, filterBehavior, generateItems, pauseWhen } from "./observable";
|
|
import { type Behavior } from "../state/Behavior";
|
|
|
|
const yesNo = {
|
|
y: true,
|
|
n: false,
|
|
};
|
|
|
|
test("or$", () => {
|
|
withTestScheduler(({ behavior, expectObservable }) => {
|
|
const input1Marbles = "ny--n--";
|
|
const input2Marbles = "n-y--n-";
|
|
const input3Marbles = "n--y--n";
|
|
const outputMarbles = "nyyyyyn";
|
|
expectObservable(
|
|
or$(
|
|
behavior(input1Marbles, yesNo),
|
|
behavior(input2Marbles, yesNo),
|
|
behavior(input3Marbles, yesNo),
|
|
),
|
|
).toBe(outputMarbles, yesNo);
|
|
});
|
|
});
|
|
|
|
test("pauseWhen", () => {
|
|
withTestScheduler(({ behavior, expectObservable }) => {
|
|
const inputMarbles = " abcdefgh-i-jk-";
|
|
const pauseMarbles = " n-y--n-yn-y--n";
|
|
const outputMarbles = "abc--fgh-i---k";
|
|
expectObservable(
|
|
behavior(inputMarbles).pipe(
|
|
pauseWhen(behavior(pauseMarbles, { y: true, n: false })),
|
|
),
|
|
).toBe(outputMarbles);
|
|
});
|
|
});
|
|
|
|
test("generateItems", () => {
|
|
const scope1$ = new Subject<string>();
|
|
const scope2$ = new Subject<string>();
|
|
const scope3$ = new Subject<string>();
|
|
const scope4$ = new Subject<string>();
|
|
const scopeSubjects = [scope1$, scope2$, scope3$, scope4$];
|
|
|
|
withTestScheduler(({ hot, expectObservable }) => {
|
|
// Each scope should start when the input number reaches or surpasses their
|
|
// number and end when the input number drops back below their number.
|
|
// At the very end, unsubscribing should end all remaining scopes.
|
|
const inputMarbles = " 123242";
|
|
const outputMarbles = " abcbdb";
|
|
const subscriptionMarbles = "^-----!";
|
|
const scope1Marbles = " y-----n";
|
|
const scope2Marbles = " -y----n";
|
|
const scope3Marbles = " --ynyn";
|
|
const scope4Marbles = " ----yn";
|
|
|
|
expectObservable(
|
|
hot<string>(inputMarbles).pipe(
|
|
generateItems(
|
|
"test items",
|
|
function* (input) {
|
|
for (let i = 1; i <= +input; i++) {
|
|
yield { keys: [i], data: undefined };
|
|
}
|
|
},
|
|
(scope, data$, i) => {
|
|
scopeSubjects[i - 1].next("y");
|
|
scope.onEnd(() => scopeSubjects[i - 1].next("n"));
|
|
return i.toString();
|
|
},
|
|
),
|
|
),
|
|
subscriptionMarbles,
|
|
).toBe(outputMarbles, {
|
|
a: ["1"],
|
|
b: ["1", "2"],
|
|
c: ["1", "2", "3"],
|
|
d: ["1", "2", "3", "4"],
|
|
});
|
|
|
|
expectObservable(scope1$).toBe(scope1Marbles);
|
|
expectObservable(scope2$).toBe(scope2Marbles);
|
|
expectObservable(scope3$).toBe(scope3Marbles);
|
|
expectObservable(scope4$).toBe(scope4Marbles);
|
|
});
|
|
});
|
|
|
|
test("filterBehavior", () => {
|
|
withTestScheduler(({ behavior, expectObservable }) => {
|
|
// Filtering the input should segment it into 2 modes of non-null behavior.
|
|
const inputMarbles = " abcxabx";
|
|
const filteredMarbles = "a--xa-x";
|
|
|
|
const input$ = behavior(inputMarbles, {
|
|
a: "a",
|
|
b: "b",
|
|
c: "c",
|
|
x: null,
|
|
});
|
|
const filtered$: Observable<Behavior<string> | null> = input$.pipe(
|
|
filterBehavior((value) => typeof value === "string"),
|
|
);
|
|
|
|
expectObservable(filtered$).toBe(filteredMarbles, {
|
|
a: expect.any(Object),
|
|
x: null,
|
|
});
|
|
expectObservable(
|
|
filtered$.pipe(
|
|
switchMap((value$) => (value$ === null ? of(null) : value$)),
|
|
),
|
|
).toBe(inputMarbles, { a: "a", b: "b", c: "c", x: null });
|
|
});
|
|
});
|