Skip to content

Commit c34eff5

Browse files
authored
Merge pull request #538 from the-draupnir-project/gnuxie/commands-unit-tests
Allow command executors to be unit tested. Part of the-draupnir-project/planning#22. Only testing Watch/Unwatch command for now to prove that it works. - Kick command will require a tiny MPS patch to expose kicking as a capability (I don't know if we're going to use the word Kick though). - Kick command also will require the throttling queue to be hooked up and working again. - Ban command should be pretty straight forward.
2 parents ecac66e + bdf0ed0 commit c34eff5

File tree

6 files changed

+245
-111
lines changed

6 files changed

+245
-111
lines changed

.github/workflows/mjolnir.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ jobs:
2929
- run: corepack yarn install
3030
- run: corepack yarn build
3131
- run: corepack yarn lint
32+
unit:
33+
name: Unit tests
34+
runs-on: ubuntu-latest
35+
steps:
36+
- uses: actions/checkout@v4
37+
- name: Specifically use node 18 like in the readme.
38+
uses: actions/setup-node@v4
39+
with:
40+
node-version: "18"
41+
- run: corepack yarn install
42+
- run: corepack yarn test:unit
3243
integration:
3344
name: Integration tests
3445
runs-on: ubuntu-latest

package.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
"remove-tests-from-lib": "rm -rf lib/test/ && cp -r lib/src/* lib/ && rm -rf lib/src/",
1515
"lint": "corepack yarn eslint src test && corepack yarn prettier --check src test",
1616
"start:dev": "corepack yarn build && node --async-stack-traces lib/index.js",
17-
"test": "ts-mocha --project ./tsconfig.json test/commands/**/*.ts",
18-
"test:integration": "NODE_ENV=harness ts-mocha --async-stack-traces --forbid-only --require test/integration/fixtures.ts --timeout 300000 --project ./tsconfig.json \"test/integration/**/*Test.ts\"",
19-
"test:integration:single": "NODE_ENV=harness corepack npx ts-mocha --require test/integration/fixtures.ts --timeout 300000 --project ./tsconfig.json",
20-
"test:appservice:integration": "NODE_ENV=harness ts-mocha --async-stack-traces --forbid-only --timeout 300000 --project ./tsconfig.json \"test/appservice/integration/**/*Test.ts\"",
21-
"test:appservice:integration:single": "NODE_ENV=harness corepack npx ts-mocha --timeout 300000 --project ./tsconfig.json",
17+
"test:unit": "mocha --require './test/tsnode.cjs' 'test/unit/**/*.{ts,tsx}'",
18+
"test:unit:single": "mocha --require test/tsnode.cjs",
19+
"test:integration": "NODE_ENV=harness mocha --require test/tsnode.cjs --async-stack-traces --forbid-only --require test/integration/fixtures.ts --timeout 300000 --project ./tsconfig.json \"test/integration/**/*Test.ts\"",
20+
"test:integration:single": "NODE_ENV=harness corepack yarn mocha --require test/tsnode.cjs --require test/integration/fixtures.ts --timeout 300000 --project ./tsconfig.json",
21+
"test:appservice:integration": "NODE_ENV=harness mocha --require test/tsnode.cjs --async-stack-traces --forbid-only --timeout 300000 --project ./tsconfig.json \"test/appservice/integration/**/*Test.ts\"",
22+
"test:appservice:integration:single": "NODE_ENV=harness corepack yarn mocha --require test/tsnode.cjs --timeout 300000 --project ./tsconfig.json",
2223
"test:manual": "NODE_ENV=harness ts-node test/integration/manualLaunchScript.ts",
2324
"version": "sed -i '/# version automated/s/[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][^\"]*/'$npm_package_version'/' synapse_antispam/setup.py && git add synapse_antispam/setup.py && cat synapse_antispam/setup.py"
2425
},
@@ -44,7 +45,8 @@
4445
"expect": "^29.7.0",
4546
"mocha": "^10.7.0",
4647
"prettier": "^3.3.3",
47-
"ts-mocha": "^10.0.0",
48+
"ts-auto-mock": "^3.7.4",
49+
"ts-node": "^10.9.2",
4850
"typescript": "^5.5.3",
4951
"typescript-eslint": "^7.16.1",
5052
"typescript-formatter": "^7.2"
@@ -54,7 +56,7 @@
5456
"@sentry/node": "^7.17.2",
5557
"@sentry/tracing": "^7.17.2",
5658
"@sinclair/typebox": "0.32.34",
57-
"@the-draupnir-project/interface-manager": "1.0.2",
59+
"@the-draupnir-project/interface-manager": "1.1.0",
5860
"@the-draupnir-project/matrix-basic-types": "^0.1.1",
5961
"await-lock": "^2.2.2",
6062
"better-sqlite3": "^9.4.3",

src/commands/WatchUnwatchCommand.ts

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,28 @@
88
// https://github.com/matrix-org/mjolnir
99
// </text>
1010

11-
import { PropagationType, isError } from "matrix-protection-suite";
11+
import {
12+
PolicyListConfig,
13+
PropagationType,
14+
RoomResolver,
15+
isError,
16+
} from "matrix-protection-suite";
1217
import {
1318
MatrixRoomReferencePresentationSchema,
1419
describeCommand,
1520
tuple,
1621
} from "@the-draupnir-project/interface-manager";
1722
import { Result } from "@gnuxie/typescript-result";
1823
import { Draupnir } from "../Draupnir";
19-
import { DraupnirInterfaceAdaptor } from "./DraupnirCommandPrerequisites";
24+
import {
25+
DraupnirContextToCommandContextTranslator,
26+
DraupnirInterfaceAdaptor,
27+
} from "./DraupnirCommandPrerequisites";
28+
29+
export type DraupnirWatchUnwatchCommandContext = {
30+
issuerManager: PolicyListConfig;
31+
roomResolver: RoomResolver;
32+
};
2033

2134
export const DraupnirWatchPolicyRoomCommand = describeCommand({
2235
summary:
@@ -26,19 +39,17 @@ export const DraupnirWatchPolicyRoomCommand = describeCommand({
2639
acceptor: MatrixRoomReferencePresentationSchema,
2740
}),
2841
async executor(
29-
draupnir: Draupnir,
42+
{ issuerManager, roomResolver }: DraupnirWatchUnwatchCommandContext,
3043
_info,
3144
_keywords,
3245
_rest,
3346
policyRoomReference
3447
): Promise<Result<void>> {
35-
const policyRoom = await draupnir.clientPlatform
36-
.toRoomResolver()
37-
.resolveRoom(policyRoomReference);
48+
const policyRoom = await roomResolver.resolveRoom(policyRoomReference);
3849
if (isError(policyRoom)) {
3950
return policyRoom;
4051
}
41-
return await draupnir.protectedRoomsSet.issuerManager.watchList(
52+
return await issuerManager.watchList(
4253
PropagationType.Direct,
4354
policyRoom.ok,
4455
{}
@@ -54,19 +65,17 @@ export const DraupnirUnwatchPolicyRoomCommand = describeCommand({
5465
acceptor: MatrixRoomReferencePresentationSchema,
5566
}),
5667
async executor(
57-
draupnir: Draupnir,
68+
{ issuerManager, roomResolver }: DraupnirWatchUnwatchCommandContext,
5869
_info,
5970
_keywords,
6071
_rest,
6172
policyRoomReference
6273
): Promise<Result<void>> {
63-
const policyRoom = await draupnir.clientPlatform
64-
.toRoomResolver()
65-
.resolveRoom(policyRoomReference);
74+
const policyRoom = await roomResolver.resolveRoom(policyRoomReference);
6675
if (isError(policyRoom)) {
6776
return policyRoom;
6877
}
69-
return await draupnir.protectedRoomsSet.issuerManager.unwatchList(
78+
return await issuerManager.unwatchList(
7079
PropagationType.Direct,
7180
policyRoom.ok
7281
);
@@ -80,4 +89,11 @@ for (const command of [
8089
DraupnirInterfaceAdaptor.describeRenderer(command, {
8190
isAlwaysSupposedToUseDefaultRenderer: true,
8291
});
92+
DraupnirContextToCommandContextTranslator.registerTranslation(
93+
command,
94+
(draupnir: Draupnir) => ({
95+
issuerManager: draupnir.protectedRoomsSet.issuerManager,
96+
roomResolver: draupnir.clientPlatform.toRoomResolver(),
97+
})
98+
);
8399
}

test/tsnode.cjs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SPDX-FileCopyrightText: 2024 Gnuxie <[email protected]>
2+
//
3+
// SPDX-License-Identifier: CC0-1.0
4+
5+
const tsAutoMockTransformer = require("ts-auto-mock/transformer").default;
6+
require("ts-node").register({
7+
project: "./tsconfig.json",
8+
transformers: (program) => ({
9+
before: [tsAutoMockTransformer(program)],
10+
}),
11+
});
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// SPDX-FileCopyrightText: 2024 Gnuxie <[email protected]>
2+
//
3+
// SPDX-License-Identifier: AFL-3.0
4+
5+
import {
6+
MatrixRoomID,
7+
MatrixRoomReference,
8+
StringRoomID,
9+
} from "@the-draupnir-project/matrix-basic-types";
10+
import expect from "expect";
11+
import {
12+
Ok,
13+
PolicyListConfig,
14+
PropagationType,
15+
RoomResolver,
16+
isOk,
17+
} from "matrix-protection-suite";
18+
import { createMock } from "ts-auto-mock";
19+
import {
20+
DraupnirUnwatchPolicyRoomCommand,
21+
DraupnirWatchPolicyRoomCommand,
22+
} from "../../../src/commands/WatchUnwatchCommand";
23+
import { CommandExecutorHelper } from "@the-draupnir-project/interface-manager";
24+
25+
describe("Test the WatchUnwatchCommmands", function () {
26+
const policyRoom = MatrixRoomReference.fromRoomID(
27+
"!room:example.com" as StringRoomID
28+
);
29+
const issuerManager = createMock<PolicyListConfig>({
30+
async watchList(propagation, room, _options) {
31+
expect(room).toBe(policyRoom);
32+
expect(propagation).toBe(PropagationType.Direct);
33+
return Ok(undefined);
34+
},
35+
});
36+
const roomResolver = createMock<RoomResolver>({
37+
async resolveRoom(roomReference) {
38+
if (roomReference instanceof MatrixRoomID) {
39+
return Ok(roomReference);
40+
}
41+
throw new TypeError(`We don't really expect to resolve anything`);
42+
},
43+
});
44+
it("DraupnirWatchCommand", async function () {
45+
const result = await CommandExecutorHelper.execute(
46+
DraupnirWatchPolicyRoomCommand,
47+
{ issuerManager, roomResolver },
48+
{},
49+
policyRoom
50+
);
51+
expect(isOk(result)).toBe(true);
52+
});
53+
it("DraupnirUnwatchCommand", async function () {
54+
const result = await CommandExecutorHelper.execute(
55+
DraupnirUnwatchPolicyRoomCommand,
56+
{ issuerManager, roomResolver },
57+
{},
58+
policyRoom
59+
);
60+
expect(isOk(result)).toBe(true);
61+
});
62+
});

0 commit comments

Comments
 (0)