chore (backend): remove mocha from dependencies
It has been unmaintained for so long :(
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"extension": ["ts","js","cjs","mjs"],
|
||||
"node-option": [
|
||||
"experimental-specifier-resolution=node",
|
||||
"loader=./test/loader.js"
|
||||
],
|
||||
"slow": 1000,
|
||||
"timeout": 30000,
|
||||
"exit": true
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import { loadConfig } from "./built/config.js";
|
||||
import { createRedisConnection } from "./built/redis.js";
|
||||
|
||||
const config = loadConfig();
|
||||
const redis = createRedisConnection(config);
|
||||
|
||||
redis.on("connect", () => redis.disconnect());
|
||||
redis.on("error", (e) => {
|
||||
throw e;
|
||||
});
|
|
@ -10,13 +10,10 @@
|
|||
"migration:run": "typeorm migration:run --dataSource ./built/ormconfig.js",
|
||||
"migration:revert": "typeorm migration:revert --dataSource ./built/ormconfig.js",
|
||||
"migration:new": "pnpm node ./scripts/create-migration.mjs",
|
||||
"check:connect": "node ./check_connect.js",
|
||||
"build": "pnpm tsc --project tsconfig.json ; pnpm tsc-alias --project tsconfig.json",
|
||||
"build:debug": "pnpm tsc --sourceMap --project tsconfig.json ; pnpm tsc-alias --project tsconfig.json",
|
||||
"watch": "pnpm tsc --project tsconfig.json --watch ; pnpm tsc-alias --project tsconfig.json --watch",
|
||||
"lint": "pnpm biome check --write **/*.ts",
|
||||
"mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
|
||||
"test": "pnpm run mocha",
|
||||
"format": "pnpm biome format * --write"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -138,7 +135,6 @@
|
|||
"@types/koa__cors": "5.0.0",
|
||||
"@types/koa__multer": "2.0.7",
|
||||
"@types/koa__router": "12.0.4",
|
||||
"@types/mocha": "10.0.7",
|
||||
"@types/node": "20.14.11",
|
||||
"@types/node-fetch": "2.6.11",
|
||||
"@types/nodemailer": "6.4.15",
|
||||
|
@ -163,7 +159,6 @@
|
|||
"@types/websocket": "1.0.10",
|
||||
"@types/ws": "8.5.11",
|
||||
"cross-env": "7.0.3",
|
||||
"mocha": "10.7.0",
|
||||
"pug": "3.0.3",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"ts-loader": "9.5.1",
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
process.env.NODE_ENV = "test";
|
||||
|
||||
import * as assert from "node:assert";
|
||||
import rndstr from "rndstr";
|
||||
import { initDb } from "../src/db/postgre.js";
|
||||
import { initTestDb } from "./utils.js";
|
||||
|
||||
describe("ActivityPub", () => {
|
||||
before(async () => {
|
||||
//await initTestDb();
|
||||
await initDb();
|
||||
});
|
||||
|
||||
describe("Parse minimum object", () => {
|
||||
const host = "https://host1.test";
|
||||
const preferredUsername = `${rndstr("A-Z", 4)}${rndstr("a-z", 4)}`;
|
||||
const actorId = `${host}/users/${preferredUsername.toLowerCase()}`;
|
||||
|
||||
const actor = {
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
id: actorId,
|
||||
type: "Person",
|
||||
preferredUsername,
|
||||
inbox: `${actorId}/inbox`,
|
||||
outbox: `${actorId}/outbox`,
|
||||
};
|
||||
|
||||
const post = {
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
id: `${host}/users/${rndstr("0-9a-z", 8)}`,
|
||||
type: "Note",
|
||||
attributedTo: actor.id,
|
||||
to: "https://www.w3.org/ns/activitystreams#Public",
|
||||
content: "あ",
|
||||
};
|
||||
|
||||
it("Minimum Actor", async () => {
|
||||
const { MockResolver } = await import("./misc/mock-resolver.js");
|
||||
const { createPerson } = await import(
|
||||
"../src/remote/activitypub/models/person.js"
|
||||
);
|
||||
|
||||
const resolver = new MockResolver();
|
||||
resolver._register(actor.id, actor);
|
||||
|
||||
const user = await createPerson(actor.id, resolver);
|
||||
|
||||
assert.deepStrictEqual(user.uri, actor.id);
|
||||
assert.deepStrictEqual(user.username, actor.preferredUsername);
|
||||
assert.deepStrictEqual(user.inbox, actor.inbox);
|
||||
});
|
||||
|
||||
it("Minimum Note", async () => {
|
||||
const { MockResolver } = await import("./misc/mock-resolver.js");
|
||||
const { createNote } = await import(
|
||||
"../src/remote/activitypub/models/note.js"
|
||||
);
|
||||
|
||||
const resolver = new MockResolver();
|
||||
resolver._register(actor.id, actor);
|
||||
resolver._register(post.id, post);
|
||||
|
||||
const note = await createNote(post.id, resolver, true);
|
||||
|
||||
assert.deepStrictEqual(note?.uri, post.id);
|
||||
assert.deepStrictEqual(note.visibility, "public");
|
||||
assert.deepStrictEqual(note.text, post.content);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Truncate long name", () => {
|
||||
const host = "https://host1.test";
|
||||
const preferredUsername = `${rndstr("A-Z", 4)}${rndstr("a-z", 4)}`;
|
||||
const actorId = `${host}/users/${preferredUsername.toLowerCase()}`;
|
||||
|
||||
const name = rndstr("0-9a-z", 129);
|
||||
|
||||
const actor = {
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
id: actorId,
|
||||
type: "Person",
|
||||
preferredUsername,
|
||||
name,
|
||||
inbox: `${actorId}/inbox`,
|
||||
outbox: `${actorId}/outbox`,
|
||||
};
|
||||
|
||||
it("Actor", async () => {
|
||||
const { MockResolver } = await import("./misc/mock-resolver.js");
|
||||
const { createPerson } = await import(
|
||||
"../src/remote/activitypub/models/person.js"
|
||||
);
|
||||
|
||||
const resolver = new MockResolver();
|
||||
resolver._register(actor.id, actor);
|
||||
|
||||
const user = await createPerson(actor.id, resolver);
|
||||
|
||||
assert.deepStrictEqual(user.name, actor.name.substr(0, 128));
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,75 +0,0 @@
|
|||
import * as assert from "node:assert";
|
||||
import httpSignature from "@peertube/http-signature";
|
||||
import { genRsaKeyPair } from "../src/misc/gen-key-pair.js";
|
||||
import {
|
||||
createSignedGet,
|
||||
createSignedPost,
|
||||
} from "../src/remote/activitypub/ap-request.js";
|
||||
|
||||
export const buildParsedSignature = (
|
||||
signingString: string,
|
||||
signature: string,
|
||||
algorithm: string,
|
||||
) => {
|
||||
return {
|
||||
scheme: "Signature",
|
||||
params: {
|
||||
keyId: "KeyID", // dummy, not used for verify
|
||||
algorithm: algorithm,
|
||||
headers: ["(request-target)", "date", "host", "digest"], // dummy, not used for verify
|
||||
signature: signature,
|
||||
},
|
||||
signingString: signingString,
|
||||
algorithm: algorithm.toUpperCase(),
|
||||
keyId: "KeyID", // dummy, not used for verify
|
||||
};
|
||||
};
|
||||
|
||||
describe("ap-request", () => {
|
||||
it("createSignedPost with verify", async () => {
|
||||
const keypair = await genRsaKeyPair();
|
||||
const key = { keyId: "x", privateKeyPem: keypair.privateKey };
|
||||
const url = "https://example.com/inbox";
|
||||
const activity = { a: 1 };
|
||||
const body = JSON.stringify(activity);
|
||||
const headers = {
|
||||
"User-Agent": "UA",
|
||||
};
|
||||
|
||||
const req = createSignedPost({
|
||||
key,
|
||||
url,
|
||||
body,
|
||||
additionalHeaders: headers,
|
||||
});
|
||||
|
||||
const parsed = buildParsedSignature(
|
||||
req.signingString,
|
||||
req.signature,
|
||||
"rsa-sha256",
|
||||
);
|
||||
|
||||
const result = httpSignature.verifySignature(parsed, keypair.publicKey);
|
||||
assert.deepStrictEqual(result, true);
|
||||
});
|
||||
|
||||
it("createSignedGet with verify", async () => {
|
||||
const keypair = await genRsaKeyPair();
|
||||
const key = { keyId: "x", privateKeyPem: keypair.privateKey };
|
||||
const url = "https://example.com/outbox";
|
||||
const headers = {
|
||||
"User-Agent": "UA",
|
||||
};
|
||||
|
||||
const req = createSignedGet({ key, url, additionalHeaders: headers });
|
||||
|
||||
const parsed = buildParsedSignature(
|
||||
req.signingString,
|
||||
req.signature,
|
||||
"rsa-sha256",
|
||||
);
|
||||
|
||||
const result = httpSignature.verifySignature(parsed, keypair.publicKey);
|
||||
assert.deepStrictEqual(result, true);
|
||||
});
|
||||
});
|
|
@ -1,535 +0,0 @@
|
|||
process.env.NODE_ENV = "test";
|
||||
|
||||
import * as assert from "node:assert";
|
||||
import type * as childProcess from "node:child_process";
|
||||
import {
|
||||
async,
|
||||
post,
|
||||
request,
|
||||
shutdownServer,
|
||||
signup,
|
||||
startServer,
|
||||
} from "./utils.js";
|
||||
|
||||
describe("API visibility", () => {
|
||||
let p: childProcess.ChildProcess;
|
||||
|
||||
before(async () => {
|
||||
p = await startServer();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await shutdownServer(p);
|
||||
});
|
||||
|
||||
describe("Note visibility", async () => {
|
||||
//#region vars
|
||||
/** ヒロイン */
|
||||
let alice: any;
|
||||
/** フォロワー */
|
||||
let follower: any;
|
||||
/** 非フォロワー */
|
||||
let other: any;
|
||||
/** 非フォロワーでもリプライやメンションをされた人 */
|
||||
let target: any;
|
||||
/** specified mentionでmentionを飛ばされる人 */
|
||||
let target2: any;
|
||||
|
||||
/** public-post */
|
||||
let pub: any;
|
||||
/** home-post */
|
||||
let home: any;
|
||||
/** followers-post */
|
||||
let fol: any;
|
||||
/** specified-post */
|
||||
let spe: any;
|
||||
|
||||
/** public-reply to target's post */
|
||||
let pubR: any;
|
||||
/** home-reply to target's post */
|
||||
let homeR: any;
|
||||
/** followers-reply to target's post */
|
||||
let folR: any;
|
||||
/** specified-reply to target's post */
|
||||
let speR: any;
|
||||
|
||||
/** public-mention to target */
|
||||
let pubM: any;
|
||||
/** home-mention to target */
|
||||
let homeM: any;
|
||||
/** followers-mention to target */
|
||||
let folM: any;
|
||||
/** specified-mention to target */
|
||||
let speM: any;
|
||||
|
||||
/** reply target post */
|
||||
let tgt: any;
|
||||
//#endregion
|
||||
|
||||
const show = async (noteId: any, by: any) => {
|
||||
return await request(
|
||||
"/notes/show",
|
||||
{
|
||||
noteId,
|
||||
},
|
||||
by,
|
||||
);
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
//#region prepare
|
||||
// signup
|
||||
alice = await signup({ username: "alice" });
|
||||
follower = await signup({ username: "follower" });
|
||||
other = await signup({ username: "other" });
|
||||
target = await signup({ username: "target" });
|
||||
target2 = await signup({ username: "target2" });
|
||||
|
||||
// follow alice <= follower
|
||||
await request("/following/create", { userId: alice.id }, follower);
|
||||
|
||||
// normal posts
|
||||
pub = await post(alice, { text: "x", visibility: "public" });
|
||||
home = await post(alice, { text: "x", visibility: "home" });
|
||||
fol = await post(alice, { text: "x", visibility: "followers" });
|
||||
spe = await post(alice, {
|
||||
text: "x",
|
||||
visibility: "specified",
|
||||
visibleUserIds: [target.id],
|
||||
});
|
||||
|
||||
// replies
|
||||
tgt = await post(target, { text: "y", visibility: "public" });
|
||||
pubR = await post(alice, {
|
||||
text: "x",
|
||||
replyId: tgt.id,
|
||||
visibility: "public",
|
||||
});
|
||||
homeR = await post(alice, {
|
||||
text: "x",
|
||||
replyId: tgt.id,
|
||||
visibility: "home",
|
||||
});
|
||||
folR = await post(alice, {
|
||||
text: "x",
|
||||
replyId: tgt.id,
|
||||
visibility: "followers",
|
||||
});
|
||||
speR = await post(alice, {
|
||||
text: "x",
|
||||
replyId: tgt.id,
|
||||
visibility: "specified",
|
||||
});
|
||||
|
||||
// mentions
|
||||
pubM = await post(alice, {
|
||||
text: "@target x",
|
||||
replyId: tgt.id,
|
||||
visibility: "public",
|
||||
});
|
||||
homeM = await post(alice, {
|
||||
text: "@target x",
|
||||
replyId: tgt.id,
|
||||
visibility: "home",
|
||||
});
|
||||
folM = await post(alice, {
|
||||
text: "@target x",
|
||||
replyId: tgt.id,
|
||||
visibility: "followers",
|
||||
});
|
||||
speM = await post(alice, {
|
||||
text: "@target2 x",
|
||||
replyId: tgt.id,
|
||||
visibility: "specified",
|
||||
});
|
||||
//#endregion
|
||||
});
|
||||
|
||||
//#region show post
|
||||
// public
|
||||
it("[show] public-postを自分が見れる", async(async () => {
|
||||
const res = await show(pub.id, alice);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] public-postをフォロワーが見れる", async(async () => {
|
||||
const res = await show(pub.id, follower);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] public-postを非フォロワーが見れる", async(async () => {
|
||||
const res = await show(pub.id, other);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] public-postを未認証が見れる", async(async () => {
|
||||
const res = await show(pub.id, null);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
// home
|
||||
it("[show] home-postを自分が見れる", async(async () => {
|
||||
const res = await show(home.id, alice);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] home-postをフォロワーが見れる", async(async () => {
|
||||
const res = await show(home.id, follower);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] home-postを非フォロワーが見れる", async(async () => {
|
||||
const res = await show(home.id, other);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] home-postを未認証が見れる", async(async () => {
|
||||
const res = await show(home.id, null);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
// followers
|
||||
it("[show] followers-postを自分が見れる", async(async () => {
|
||||
const res = await show(fol.id, alice);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] followers-postをフォロワーが見れる", async(async () => {
|
||||
const res = await show(fol.id, follower);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] followers-postを非フォロワーが見れない", async(async () => {
|
||||
const res = await show(fol.id, other);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
it("[show] followers-postを未認証が見れない", async(async () => {
|
||||
const res = await show(fol.id, null);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
// specified
|
||||
it("[show] specified-postを自分が見れる", async(async () => {
|
||||
const res = await show(spe.id, alice);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
it("[show] specified-postを指定ユーザーが見れる", async(async () => {
|
||||
const res = await show(spe.id, target);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] specified-postをフォロワーが見れない", async(async () => {
|
||||
const res = await show(spe.id, follower);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
it("[show] specified-postを非フォロワーが見れない", async(async () => {
|
||||
const res = await show(spe.id, other);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
it("[show] specified-postを未認証が見れない", async(async () => {
|
||||
const res = await show(spe.id, null);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
//#endregion
|
||||
|
||||
//#region show reply
|
||||
// public
|
||||
it("[show] public-replyを自分が見れる", async(async () => {
|
||||
const res = await show(pubR.id, alice);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] public-replyをされた人が見れる", async(async () => {
|
||||
const res = await show(pubR.id, target);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] public-replyをフォロワーが見れる", async(async () => {
|
||||
const res = await show(pubR.id, follower);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] public-replyを非フォロワーが見れる", async(async () => {
|
||||
const res = await show(pubR.id, other);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] public-replyを未認証が見れる", async(async () => {
|
||||
const res = await show(pubR.id, null);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
// home
|
||||
it("[show] home-replyを自分が見れる", async(async () => {
|
||||
const res = await show(homeR.id, alice);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] home-replyをされた人が見れる", async(async () => {
|
||||
const res = await show(homeR.id, target);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] home-replyをフォロワーが見れる", async(async () => {
|
||||
const res = await show(homeR.id, follower);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] home-replyを非フォロワーが見れる", async(async () => {
|
||||
const res = await show(homeR.id, other);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] home-replyを未認証が見れる", async(async () => {
|
||||
const res = await show(homeR.id, null);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
// followers
|
||||
it("[show] followers-replyを自分が見れる", async(async () => {
|
||||
const res = await show(folR.id, alice);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] followers-replyを非フォロワーでもリプライされていれば見れる", async(async () => {
|
||||
const res = await show(folR.id, target);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] followers-replyをフォロワーが見れる", async(async () => {
|
||||
const res = await show(folR.id, follower);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] followers-replyを非フォロワーが見れない", async(async () => {
|
||||
const res = await show(folR.id, other);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
it("[show] followers-replyを未認証が見れない", async(async () => {
|
||||
const res = await show(folR.id, null);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
// specified
|
||||
it("[show] specified-replyを自分が見れる", async(async () => {
|
||||
const res = await show(speR.id, alice);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] specified-replyを指定ユーザーが見れる", async(async () => {
|
||||
const res = await show(speR.id, target);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] specified-replyをされた人が指定されてなくても見れる", async(async () => {
|
||||
const res = await show(speR.id, target);
|
||||
assert.strictEqual(res.body.text, "x");
|
||||
}));
|
||||
|
||||
it("[show] specified-replyをフォロワーが見れない", async(async () => {
|
||||
const res = await show(speR.id, follower);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
it("[show] specified-replyを非フォロワーが見れない", async(async () => {
|
||||
const res = await show(speR.id, other);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
it("[show] specified-replyを未認証が見れない", async(async () => {
|
||||
const res = await show(speR.id, null);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
//#endregion
|
||||
|
||||
//#region show mention
|
||||
// public
|
||||
it("[show] public-mentionを自分が見れる", async(async () => {
|
||||
const res = await show(pubM.id, alice);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
it("[show] public-mentionをされた人が見れる", async(async () => {
|
||||
const res = await show(pubM.id, target);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
it("[show] public-mentionをフォロワーが見れる", async(async () => {
|
||||
const res = await show(pubM.id, follower);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
it("[show] public-mentionを非フォロワーが見れる", async(async () => {
|
||||
const res = await show(pubM.id, other);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
it("[show] public-mentionを未認証が見れる", async(async () => {
|
||||
const res = await show(pubM.id, null);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
// home
|
||||
it("[show] home-mentionを自分が見れる", async(async () => {
|
||||
const res = await show(homeM.id, alice);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
it("[show] home-mentionをされた人が見れる", async(async () => {
|
||||
const res = await show(homeM.id, target);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
it("[show] home-mentionをフォロワーが見れる", async(async () => {
|
||||
const res = await show(homeM.id, follower);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
it("[show] home-mentionを非フォロワーが見れる", async(async () => {
|
||||
const res = await show(homeM.id, other);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
it("[show] home-mentionを未認証が見れる", async(async () => {
|
||||
const res = await show(homeM.id, null);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
// followers
|
||||
it("[show] followers-mentionを自分が見れる", async(async () => {
|
||||
const res = await show(folM.id, alice);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
it("[show] followers-mentionをメンションされていれば非フォロワーでも見れる", async(async () => {
|
||||
const res = await show(folM.id, target);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
it("[show] followers-mentionをフォロワーが見れる", async(async () => {
|
||||
const res = await show(folM.id, follower);
|
||||
assert.strictEqual(res.body.text, "@target x");
|
||||
}));
|
||||
|
||||
it("[show] followers-mentionを非フォロワーが見れない", async(async () => {
|
||||
const res = await show(folM.id, other);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
it("[show] followers-mentionを未認証が見れない", async(async () => {
|
||||
const res = await show(folM.id, null);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
// specified
|
||||
it("[show] specified-mentionを自分が見れる", async(async () => {
|
||||
const res = await show(speM.id, alice);
|
||||
assert.strictEqual(res.body.text, "@target2 x");
|
||||
}));
|
||||
|
||||
it("[show] specified-mentionを指定ユーザーが見れる", async(async () => {
|
||||
const res = await show(speM.id, target);
|
||||
assert.strictEqual(res.body.text, "@target2 x");
|
||||
}));
|
||||
|
||||
it("[show] specified-mentionをされた人が指定されてなかったら見れない", async(async () => {
|
||||
const res = await show(speM.id, target2);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
it("[show] specified-mentionをフォロワーが見れない", async(async () => {
|
||||
const res = await show(speM.id, follower);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
it("[show] specified-mentionを非フォロワーが見れない", async(async () => {
|
||||
const res = await show(speM.id, other);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
|
||||
it("[show] specified-mentionを未認証が見れない", async(async () => {
|
||||
const res = await show(speM.id, null);
|
||||
assert.strictEqual(res.status, 404);
|
||||
}));
|
||||
//#endregion
|
||||
|
||||
//#region HTL
|
||||
it("[HTL] public-post が 自分が見れる", async(async () => {
|
||||
const res = await request("/notes/timeline", { limit: 100 }, alice);
|
||||
assert.strictEqual(res.status, 200);
|
||||
const notes = res.body.filter((n: any) => n.id == pub.id);
|
||||
assert.strictEqual(notes[0].text, "x");
|
||||
}));
|
||||
|
||||
it("[HTL] public-post が 非フォロワーから見れない", async(async () => {
|
||||
const res = await request("/notes/timeline", { limit: 100 }, other);
|
||||
assert.strictEqual(res.status, 200);
|
||||
const notes = res.body.filter((n: any) => n.id == pub.id);
|
||||
assert.strictEqual(notes.length, 0);
|
||||
}));
|
||||
|
||||
it("[HTL] followers-post が フォロワーから見れる", async(async () => {
|
||||
const res = await request("/notes/timeline", { limit: 100 }, follower);
|
||||
assert.strictEqual(res.status, 200);
|
||||
const notes = res.body.filter((n: any) => n.id == fol.id);
|
||||
assert.strictEqual(notes[0].text, "x");
|
||||
}));
|
||||
//#endregion
|
||||
|
||||
//#region RTL
|
||||
it("[replies] followers-reply が フォロワーから見れる", async(async () => {
|
||||
const res = await request(
|
||||
"/notes/replies",
|
||||
{ noteId: tgt.id, limit: 100 },
|
||||
follower,
|
||||
);
|
||||
assert.strictEqual(res.status, 200);
|
||||
const notes = res.body.filter((n: any) => n.id == folR.id);
|
||||
assert.strictEqual(notes[0].text, "x");
|
||||
}));
|
||||
|
||||
it("[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない", async(async () => {
|
||||
const res = await request(
|
||||
"/notes/replies",
|
||||
{ noteId: tgt.id, limit: 100 },
|
||||
other,
|
||||
);
|
||||
assert.strictEqual(res.status, 200);
|
||||
const notes = res.body.filter((n: any) => n.id == folR.id);
|
||||
assert.strictEqual(notes.length, 0);
|
||||
}));
|
||||
|
||||
it("[replies] followers-reply が 非フォロワー (リプライ先である) から見れる", async(async () => {
|
||||
const res = await request(
|
||||
"/notes/replies",
|
||||
{ noteId: tgt.id, limit: 100 },
|
||||
target,
|
||||
);
|
||||
assert.strictEqual(res.status, 200);
|
||||
const notes = res.body.filter((n: any) => n.id == folR.id);
|
||||
assert.strictEqual(notes[0].text, "x");
|
||||
}));
|
||||
//#endregion
|
||||
|
||||
//#region MTL
|
||||
it("[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる", async(async () => {
|
||||
const res = await request("/notes/mentions", { limit: 100 }, target);
|
||||
assert.strictEqual(res.status, 200);
|
||||
const notes = res.body.filter((n: any) => n.id == folR.id);
|
||||
assert.strictEqual(notes[0].text, "x");
|
||||
}));
|
||||
|
||||
it("[mentions] followers-mention が 非フォロワー (メンション先である) から見れる", async(async () => {
|
||||
const res = await request("/notes/mentions", { limit: 100 }, target);
|
||||
assert.strictEqual(res.status, 200);
|
||||
const notes = res.body.filter((n: any) => n.id == folM.id);
|
||||
assert.strictEqual(notes[0].text, "@target x");
|
||||
}));
|
||||
//#endregion
|
||||
});
|
||||
});
|
|
@ -1,92 +0,0 @@
|
|||
process.env.NODE_ENV = "test";
|
||||
|
||||
import * as assert from "node:assert";
|
||||
import type * as childProcess from "node:child_process";
|
||||
import {
|
||||
async,
|
||||
post,
|
||||
react,
|
||||
request,
|
||||
shutdownServer,
|
||||
signup,
|
||||
startServer,
|
||||
uploadFile,
|
||||
} from "./utils.js";
|
||||
|
||||
describe("API", () => {
|
||||
let p: childProcess.ChildProcess;
|
||||
let alice: any;
|
||||
let bob: any;
|
||||
let carol: any;
|
||||
|
||||
before(async () => {
|
||||
p = await startServer();
|
||||
alice = await signup({ username: "alice" });
|
||||
bob = await signup({ username: "bob" });
|
||||
carol = await signup({ username: "carol" });
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await shutdownServer(p);
|
||||
});
|
||||
|
||||
describe("General validation", () => {
|
||||
it("wrong type", async(async () => {
|
||||
const res = await request("/test", {
|
||||
required: true,
|
||||
string: 42,
|
||||
});
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it("missing require param", async(async () => {
|
||||
const res = await request("/test", {
|
||||
string: "a",
|
||||
});
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it("invalid misskey:id (empty string)", async(async () => {
|
||||
const res = await request("/test", {
|
||||
required: true,
|
||||
id: "",
|
||||
});
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it("valid misskey:id", async(async () => {
|
||||
const res = await request("/test", {
|
||||
required: true,
|
||||
id: "8wvhjghbxu",
|
||||
});
|
||||
assert.strictEqual(res.status, 200);
|
||||
}));
|
||||
|
||||
it("default value", async(async () => {
|
||||
const res = await request("/test", {
|
||||
required: true,
|
||||
string: "a",
|
||||
});
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.body.default, "hello");
|
||||
}));
|
||||
|
||||
it("can set null even if it has default value", async(async () => {
|
||||
const res = await request("/test", {
|
||||
required: true,
|
||||
nullableDefault: null,
|
||||
});
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.body.nullableDefault, null);
|
||||
}));
|
||||
|
||||
it("cannot set undefined if it has default value", async(async () => {
|
||||
const res = await request("/test", {
|
||||
required: true,
|
||||
nullableDefault: undefined,
|
||||
});
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.body.nullableDefault, "hello");
|
||||
}));
|
||||
});
|
||||
});
|
|
@ -1,129 +0,0 @@
|
|||
process.env.NODE_ENV = "test";
|
||||
|
||||
import * as assert from "node:assert";
|
||||
import type * as childProcess from "node:child_process";
|
||||
import {
|
||||
async,
|
||||
post,
|
||||
request,
|
||||
shutdownServer,
|
||||
signup,
|
||||
startServer,
|
||||
} from "./utils.js";
|
||||
|
||||
describe("Block", () => {
|
||||
let p: childProcess.ChildProcess;
|
||||
|
||||
// alice blocks bob
|
||||
let alice: any;
|
||||
let bob: any;
|
||||
let carol: any;
|
||||
|
||||
before(async () => {
|
||||
p = await startServer();
|
||||
alice = await signup({ username: "alice" });
|
||||
bob = await signup({ username: "bob" });
|
||||
carol = await signup({ username: "carol" });
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await shutdownServer(p);
|
||||
});
|
||||
|
||||
it("Block作成", async(async () => {
|
||||
const res = await request(
|
||||
"/blocking/create",
|
||||
{
|
||||
userId: bob.id,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
}));
|
||||
|
||||
it("ブロックされているユーザーをフォローできない", async(async () => {
|
||||
const res = await request("/following/create", { userId: alice.id }, bob);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
assert.strictEqual(
|
||||
res.body.error.id,
|
||||
"c4ab57cc-4e41-45e9-bfd9-584f61e35ce0",
|
||||
);
|
||||
}));
|
||||
|
||||
it("ブロックされているユーザーにリアクションできない", async(async () => {
|
||||
const note = await post(alice, { text: "hello" });
|
||||
|
||||
const res = await request(
|
||||
"/notes/reactions/create",
|
||||
{ noteId: note.id, reaction: "👍" },
|
||||
bob,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
assert.strictEqual(
|
||||
res.body.error.id,
|
||||
"20ef5475-9f38-4e4c-bd33-de6d979498ec",
|
||||
);
|
||||
}));
|
||||
|
||||
it("ブロックされているユーザーに返信できない", async(async () => {
|
||||
const note = await post(alice, { text: "hello" });
|
||||
|
||||
const res = await request(
|
||||
"/notes/create",
|
||||
{ replyId: note.id, text: "yo" },
|
||||
bob,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
assert.strictEqual(
|
||||
res.body.error.id,
|
||||
"b390d7e1-8a5e-46ed-b625-06271cafd3d3",
|
||||
);
|
||||
}));
|
||||
|
||||
it("ブロックされているユーザーのノートをRenoteできない", async(async () => {
|
||||
const note = await post(alice, { text: "hello" });
|
||||
|
||||
const res = await request(
|
||||
"/notes/create",
|
||||
{ renoteId: note.id, text: "yo" },
|
||||
bob,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
assert.strictEqual(
|
||||
res.body.error.id,
|
||||
"b390d7e1-8a5e-46ed-b625-06271cafd3d3",
|
||||
);
|
||||
}));
|
||||
|
||||
// TODO: ユーザーリストに入れられないテスト
|
||||
|
||||
// TODO: ユーザーリストから除外されるテスト
|
||||
|
||||
it("タイムライン(LTL)にブロックされているユーザーの投稿が含まれない", async(async () => {
|
||||
const aliceNote = await post(alice);
|
||||
const bobNote = await post(bob);
|
||||
const carolNote = await post(carol);
|
||||
|
||||
const res = await request("/notes/local-timeline", {}, bob);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === aliceNote.id),
|
||||
false,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === bobNote.id),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === carolNote.id),
|
||||
true,
|
||||
);
|
||||
}));
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
redistest:
|
||||
image: redis:6
|
||||
ports:
|
||||
- "127.0.0.1:56312:6379"
|
||||
|
||||
dbtest:
|
||||
image: postgres:13
|
||||
ports:
|
||||
- "127.0.0.1:54312:5432"
|
||||
environment:
|
||||
POSTGRES_DB: "test-misskey"
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
|
@ -1,865 +0,0 @@
|
|||
/*
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as childProcess from 'child_process';
|
||||
import { async, signup, request, post, react, uploadFile, startServer, shutdownServer } from './utils.js';
|
||||
|
||||
describe('API: Endpoints', () => {
|
||||
let p: childProcess.ChildProcess;
|
||||
let alice: any;
|
||||
let bob: any;
|
||||
let carol: any;
|
||||
|
||||
before(async () => {
|
||||
p = await startServer();
|
||||
alice = await signup({ username: 'alice' });
|
||||
bob = await signup({ username: 'bob' });
|
||||
carol = await signup({ username: 'carol' });
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await shutdownServer(p);
|
||||
});
|
||||
|
||||
describe('signup', () => {
|
||||
it('不正なユーザー名でアカウントが作成できない', async(async () => {
|
||||
const res = await request('/signup', {
|
||||
username: 'test.',
|
||||
password: 'test'
|
||||
});
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('空のパスワードでアカウントが作成できない', async(async () => {
|
||||
const res = await request('/signup', {
|
||||
username: 'test',
|
||||
password: ''
|
||||
});
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('正しくアカウントが作成できる', async(async () => {
|
||||
const me = {
|
||||
username: 'test1',
|
||||
password: 'test1'
|
||||
};
|
||||
|
||||
const res = await request('/signup', me);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.username, me.username);
|
||||
}));
|
||||
|
||||
it('同じユーザー名のアカウントは作成できない', async(async () => {
|
||||
await signup({
|
||||
username: 'test2'
|
||||
});
|
||||
|
||||
const res = await request('/signup', {
|
||||
username: 'test2',
|
||||
password: 'test2'
|
||||
});
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('signin', () => {
|
||||
it('間違ったパスワードでサインインできない', async(async () => {
|
||||
await signup({
|
||||
username: 'test3',
|
||||
password: 'foo'
|
||||
});
|
||||
|
||||
const res = await request('/signin', {
|
||||
username: 'test3',
|
||||
password: 'bar'
|
||||
});
|
||||
|
||||
assert.strictEqual(res.status, 403);
|
||||
}));
|
||||
|
||||
it('クエリをインジェクションできない', async(async () => {
|
||||
await signup({
|
||||
username: 'test4'
|
||||
});
|
||||
|
||||
const res = await request('/signin', {
|
||||
username: 'test4',
|
||||
password: {
|
||||
$gt: ''
|
||||
}
|
||||
});
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('正しい情報でサインインできる', async(async () => {
|
||||
await signup({
|
||||
username: 'test5',
|
||||
password: 'foo'
|
||||
});
|
||||
|
||||
const res = await request('/signin', {
|
||||
username: 'test5',
|
||||
password: 'foo'
|
||||
});
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('i/update', () => {
|
||||
it('アカウント設定を更新できる', async(async () => {
|
||||
const myName = '大室櫻子';
|
||||
const myLocation = '七森中';
|
||||
const myBirthday = '2000-09-07';
|
||||
|
||||
const res = await request('/i/update', {
|
||||
name: myName,
|
||||
location: myLocation,
|
||||
birthday: myBirthday
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.name, myName);
|
||||
assert.strictEqual(res.body.location, myLocation);
|
||||
assert.strictEqual(res.body.birthday, myBirthday);
|
||||
}));
|
||||
|
||||
it('名前を空白にできない', async(async () => {
|
||||
const res = await request('/i/update', {
|
||||
name: ' '
|
||||
}, alice);
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('誕生日の設定を削除できる', async(async () => {
|
||||
await request('/i/update', {
|
||||
birthday: '2000-09-07'
|
||||
}, alice);
|
||||
|
||||
const res = await request('/i/update', {
|
||||
birthday: null
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.birthday, null);
|
||||
}));
|
||||
|
||||
it('不正な誕生日の形式で怒られる', async(async () => {
|
||||
const res = await request('/i/update', {
|
||||
birthday: '2000/09/07'
|
||||
}, alice);
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('users/show', () => {
|
||||
it('ユーザーが取得できる', async(async () => {
|
||||
const res = await request('/users/show', {
|
||||
userId: alice.id
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.id, alice.id);
|
||||
}));
|
||||
|
||||
it('ユーザーが存在しなかったら怒る', async(async () => {
|
||||
const res = await request('/users/show', {
|
||||
userId: '000000000000000000000000'
|
||||
});
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('間違ったIDで怒られる', async(async () => {
|
||||
const res = await request('/users/show', {
|
||||
userId: 'kyoppie'
|
||||
});
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('notes/show', () => {
|
||||
it('投稿が取得できる', async(async () => {
|
||||
const myPost = await post(alice, {
|
||||
text: 'test'
|
||||
});
|
||||
|
||||
const res = await request('/notes/show', {
|
||||
noteId: myPost.id
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.id, myPost.id);
|
||||
assert.strictEqual(res.body.text, myPost.text);
|
||||
}));
|
||||
|
||||
it('投稿が存在しなかったら怒る', async(async () => {
|
||||
const res = await request('/notes/show', {
|
||||
noteId: '000000000000000000000000'
|
||||
});
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('間違ったIDで怒られる', async(async () => {
|
||||
const res = await request('/notes/show', {
|
||||
noteId: 'kyoppie'
|
||||
});
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('notes/reactions/create', () => {
|
||||
it('リアクションできる', async(async () => {
|
||||
const bobPost = await post(bob);
|
||||
|
||||
const alice = await signup({ username: 'alice' });
|
||||
const res = await request('/notes/reactions/create', {
|
||||
noteId: bobPost.id,
|
||||
reaction: '🚀',
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 204);
|
||||
|
||||
const resNote = await request('/notes/show', {
|
||||
noteId: bobPost.id,
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(resNote.status, 200);
|
||||
assert.strictEqual(resNote.body.reactions['🚀'], [alice.id]);
|
||||
}));
|
||||
|
||||
it('自分の投稿にもリアクションできる', async(async () => {
|
||||
const myPost = await post(alice);
|
||||
|
||||
const res = await request('/notes/reactions/create', {
|
||||
noteId: myPost.id,
|
||||
reaction: '🚀',
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 204);
|
||||
}));
|
||||
|
||||
it('二重にリアクションできない', async(async () => {
|
||||
const bobPost = await post(bob);
|
||||
|
||||
await react(alice, bobPost, 'like');
|
||||
|
||||
const res = await request('/notes/reactions/create', {
|
||||
noteId: bobPost.id,
|
||||
reaction: '🚀',
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('存在しない投稿にはリアクションできない', async(async () => {
|
||||
const res = await request('/notes/reactions/create', {
|
||||
noteId: '000000000000000000000000',
|
||||
reaction: '🚀',
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('空のパラメータで怒られる', async(async () => {
|
||||
const res = await request('/notes/reactions/create', {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('間違ったIDで怒られる', async(async () => {
|
||||
const res = await request('/notes/reactions/create', {
|
||||
noteId: 'kyoppie',
|
||||
reaction: '🚀',
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('following/create', () => {
|
||||
it('フォローできる', async(async () => {
|
||||
const res = await request('/following/create', {
|
||||
userId: alice.id
|
||||
}, bob);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
}));
|
||||
|
||||
it('既にフォローしている場合は怒る', async(async () => {
|
||||
const res = await request('/following/create', {
|
||||
userId: alice.id
|
||||
}, bob);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('存在しないユーザーはフォローできない', async(async () => {
|
||||
const res = await request('/following/create', {
|
||||
userId: '000000000000000000000000'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('自分自身はフォローできない', async(async () => {
|
||||
const res = await request('/following/create', {
|
||||
userId: alice.id
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('空のパラメータで怒られる', async(async () => {
|
||||
const res = await request('/following/create', {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('間違ったIDで怒られる', async(async () => {
|
||||
const res = await request('/following/create', {
|
||||
userId: 'foo'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('following/delete', () => {
|
||||
it('フォロー解除できる', async(async () => {
|
||||
await request('/following/create', {
|
||||
userId: alice.id
|
||||
}, bob);
|
||||
|
||||
const res = await request('/following/delete', {
|
||||
userId: alice.id
|
||||
}, bob);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
}));
|
||||
|
||||
it('フォローしていない場合は怒る', async(async () => {
|
||||
const res = await request('/following/delete', {
|
||||
userId: alice.id
|
||||
}, bob);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('存在しないユーザーはフォロー解除できない', async(async () => {
|
||||
const res = await request('/following/delete', {
|
||||
userId: '000000000000000000000000'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('自分自身はフォロー解除できない', async(async () => {
|
||||
const res = await request('/following/delete', {
|
||||
userId: alice.id
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('空のパラメータで怒られる', async(async () => {
|
||||
const res = await request('/following/delete', {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('間違ったIDで怒られる', async(async () => {
|
||||
const res = await request('/following/delete', {
|
||||
userId: 'kyoppie'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('drive', () => {
|
||||
it('ドライブ情報を取得できる', async(async () => {
|
||||
await uploadFile({
|
||||
userId: alice.id,
|
||||
size: 256
|
||||
});
|
||||
await uploadFile({
|
||||
userId: alice.id,
|
||||
size: 512
|
||||
});
|
||||
await uploadFile({
|
||||
userId: alice.id,
|
||||
size: 1024
|
||||
});
|
||||
const res = await request('/drive', {}, alice);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
expect(res.body).have.property('usage').eql(1792);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('drive/files/create', () => {
|
||||
it('ファイルを作成できる', async(async () => {
|
||||
const res = await uploadFile(alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.name, 'Lenna.png');
|
||||
}));
|
||||
|
||||
it('ファイルに名前を付けられる', async(async () => {
|
||||
const res = await assert.request(server)
|
||||
.post('/drive/files/create')
|
||||
.field('i', alice.token)
|
||||
.field('name', 'Belmond.png')
|
||||
.attach('file', fs.readFileSync(__dirname + '/resources/Lenna.png'), 'Lenna.png');
|
||||
|
||||
expect(res).have.status(200);
|
||||
expect(res.body).be.a('object');
|
||||
expect(res.body).have.property('name').eql('Belmond.png');
|
||||
}));
|
||||
|
||||
it('ファイル無しで怒られる', async(async () => {
|
||||
const res = await request('/drive/files/create', {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('SVGファイルを作成できる', async(async () => {
|
||||
const res = await uploadFile(alice, __dirname + '/resources/image.svg');
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.name, 'image.svg');
|
||||
assert.strictEqual(res.body.type, 'image/svg+xml');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('drive/files/update', () => {
|
||||
it('名前を更新できる', async(async () => {
|
||||
const file = await uploadFile(alice);
|
||||
const newName = 'いちごパスタ.png';
|
||||
|
||||
const res = await request('/drive/files/update', {
|
||||
fileId: file.id,
|
||||
name: newName
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.name, newName);
|
||||
}));
|
||||
|
||||
it('他人のファイルは更新できない', async(async () => {
|
||||
const file = await uploadFile(bob);
|
||||
|
||||
const res = await request('/drive/files/update', {
|
||||
fileId: file.id,
|
||||
name: 'いちごパスタ.png'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('親フォルダを更新できる', async(async () => {
|
||||
const file = await uploadFile(alice);
|
||||
const folder = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
|
||||
const res = await request('/drive/files/update', {
|
||||
fileId: file.id,
|
||||
folderId: folder.id
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.folderId, folder.id);
|
||||
}));
|
||||
|
||||
it('親フォルダを無しにできる', async(async () => {
|
||||
const file = await uploadFile(alice);
|
||||
|
||||
const folder = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
|
||||
await request('/drive/files/update', {
|
||||
fileId: file.id,
|
||||
folderId: folder.id
|
||||
}, alice);
|
||||
|
||||
const res = await request('/drive/files/update', {
|
||||
fileId: file.id,
|
||||
folderId: null
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.folderId, null);
|
||||
}));
|
||||
|
||||
it('他人のフォルダには入れられない', async(async () => {
|
||||
const file = await uploadFile(alice);
|
||||
const folder = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, bob)).body;
|
||||
|
||||
const res = await request('/drive/files/update', {
|
||||
fileId: file.id,
|
||||
folderId: folder.id
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('存在しないフォルダで怒られる', async(async () => {
|
||||
const file = await uploadFile(alice);
|
||||
|
||||
const res = await request('/drive/files/update', {
|
||||
fileId: file.id,
|
||||
folderId: '000000000000000000000000'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('不正なフォルダIDで怒られる', async(async () => {
|
||||
const file = await uploadFile(alice);
|
||||
|
||||
const res = await request('/drive/files/update', {
|
||||
fileId: file.id,
|
||||
folderId: 'foo'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('ファイルが存在しなかったら怒る', async(async () => {
|
||||
const res = await request('/drive/files/update', {
|
||||
fileId: '000000000000000000000000',
|
||||
name: 'いちごパスタ.png'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('間違ったIDで怒られる', async(async () => {
|
||||
const res = await request('/drive/files/update', {
|
||||
fileId: 'kyoppie',
|
||||
name: 'いちごパスタ.png'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('drive/folders/create', () => {
|
||||
it('フォルダを作成できる', async(async () => {
|
||||
const res = await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.name, 'test');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('drive/folders/update', () => {
|
||||
it('名前を更新できる', async(async () => {
|
||||
const folder = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
|
||||
const res = await request('/drive/folders/update', {
|
||||
folderId: folder.id,
|
||||
name: 'new name'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.name, 'new name');
|
||||
}));
|
||||
|
||||
it('他人のフォルダを更新できない', async(async () => {
|
||||
const folder = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, bob)).body;
|
||||
|
||||
const res = await request('/drive/folders/update', {
|
||||
folderId: folder.id,
|
||||
name: 'new name'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('親フォルダを更新できる', async(async () => {
|
||||
const folder = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
const parentFolder = (await request('/drive/folders/create', {
|
||||
name: 'parent'
|
||||
}, alice)).body;
|
||||
|
||||
const res = await request('/drive/folders/update', {
|
||||
folderId: folder.id,
|
||||
parentId: parentFolder.id
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.parentId, parentFolder.id);
|
||||
}));
|
||||
|
||||
it('親フォルダを無しに更新できる', async(async () => {
|
||||
const folder = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
const parentFolder = (await request('/drive/folders/create', {
|
||||
name: 'parent'
|
||||
}, alice)).body;
|
||||
await request('/drive/folders/update', {
|
||||
folderId: folder.id,
|
||||
parentId: parentFolder.id
|
||||
}, alice);
|
||||
|
||||
const res = await request('/drive/folders/update', {
|
||||
folderId: folder.id,
|
||||
parentId: null
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.parentId, null);
|
||||
}));
|
||||
|
||||
it('他人のフォルダを親フォルダに設定できない', async(async () => {
|
||||
const folder = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
const parentFolder = (await request('/drive/folders/create', {
|
||||
name: 'parent'
|
||||
}, bob)).body;
|
||||
|
||||
const res = await request('/drive/folders/update', {
|
||||
folderId: folder.id,
|
||||
parentId: parentFolder.id
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('フォルダが循環するような構造にできない', async(async () => {
|
||||
const folder = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
const parentFolder = (await request('/drive/folders/create', {
|
||||
name: 'parent'
|
||||
}, alice)).body;
|
||||
await request('/drive/folders/update', {
|
||||
folderId: parentFolder.id,
|
||||
parentId: folder.id
|
||||
}, alice);
|
||||
|
||||
const res = await request('/drive/folders/update', {
|
||||
folderId: folder.id,
|
||||
parentId: parentFolder.id
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('フォルダが循環するような構造にできない(再帰的)', async(async () => {
|
||||
const folderA = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
const folderB = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
const folderC = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
await request('/drive/folders/update', {
|
||||
folderId: folderB.id,
|
||||
parentId: folderA.id
|
||||
}, alice);
|
||||
await request('/drive/folders/update', {
|
||||
folderId: folderC.id,
|
||||
parentId: folderB.id
|
||||
}, alice);
|
||||
|
||||
const res = await request('/drive/folders/update', {
|
||||
folderId: folderA.id,
|
||||
parentId: folderC.id
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('フォルダが循環するような構造にできない(自身)', async(async () => {
|
||||
const folderA = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
|
||||
const res = await request('/drive/folders/update', {
|
||||
folderId: folderA.id,
|
||||
parentId: folderA.id
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('存在しない親フォルダを設定できない', async(async () => {
|
||||
const folder = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
|
||||
const res = await request('/drive/folders/update', {
|
||||
folderId: folder.id,
|
||||
parentId: '000000000000000000000000'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('不正な親フォルダIDで怒られる', async(async () => {
|
||||
const folder = (await request('/drive/folders/create', {
|
||||
name: 'test'
|
||||
}, alice)).body;
|
||||
|
||||
const res = await request('/drive/folders/update', {
|
||||
folderId: folder.id,
|
||||
parentId: 'foo'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('存在しないフォルダを更新できない', async(async () => {
|
||||
const res = await request('/drive/folders/update', {
|
||||
folderId: '000000000000000000000000'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('不正なフォルダIDで怒られる', async(async () => {
|
||||
const res = await request('/drive/folders/update', {
|
||||
folderId: 'foo'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('messaging/messages/create', () => {
|
||||
it('メッセージを送信できる', async(async () => {
|
||||
const res = await request('/messaging/messages/create', {
|
||||
userId: bob.id,
|
||||
text: 'test'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.text, 'test');
|
||||
}));
|
||||
|
||||
it('自分自身にはメッセージを送信できない', async(async () => {
|
||||
const res = await request('/messaging/messages/create', {
|
||||
userId: alice.id,
|
||||
text: 'Yo'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('存在しないユーザーにはメッセージを送信できない', async(async () => {
|
||||
const res = await request('/messaging/messages/create', {
|
||||
userId: '000000000000000000000000',
|
||||
text: 'test'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('不正なユーザーIDで怒られる', async(async () => {
|
||||
const res = await request('/messaging/messages/create', {
|
||||
userId: 'foo',
|
||||
text: 'test'
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('テキストが無くて怒られる', async(async () => {
|
||||
const res = await request('/messaging/messages/create', {
|
||||
userId: bob.id
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it('文字数オーバーで怒られる', async(async () => {
|
||||
const res = await request('/messaging/messages/create', {
|
||||
userId: bob.id,
|
||||
text: '!'.repeat(1001)
|
||||
}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('notes/replies', () => {
|
||||
it('自分に閲覧権限のない投稿は含まれない', async(async () => {
|
||||
const alicePost = await post(alice, {
|
||||
text: 'foo'
|
||||
});
|
||||
|
||||
await post(bob, {
|
||||
replyId: alicePost.id,
|
||||
text: 'bar',
|
||||
visibility: 'specified',
|
||||
visibleUserIds: [alice.id]
|
||||
});
|
||||
|
||||
const res = await request('/notes/replies', {
|
||||
noteId: alicePost.id
|
||||
}, carol);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.length, 0);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('notes/timeline', () => {
|
||||
it('フォロワー限定投稿が含まれる', async(async () => {
|
||||
await request('/following/create', {
|
||||
userId: alice.id
|
||||
}, bob);
|
||||
|
||||
const alicePost = await post(alice, {
|
||||
text: 'foo',
|
||||
visibility: 'followers'
|
||||
});
|
||||
|
||||
const res = await request('/notes/timeline', {}, bob);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.length, 1);
|
||||
assert.strictEqual(res.body[0].id, alicePost.id);
|
||||
}));
|
||||
});
|
||||
});
|
||||
*/
|
|
@ -1,50 +0,0 @@
|
|||
import * as assert from "node:assert";
|
||||
|
||||
import { parse } from "mfm-js";
|
||||
import { extractMentions } from "../src/misc/extract-mentions.js";
|
||||
|
||||
describe("Extract mentions", () => {
|
||||
it("simple", () => {
|
||||
const ast = parse("@foo @bar @baz")!;
|
||||
const mentions = extractMentions(ast);
|
||||
assert.deepStrictEqual(mentions, [
|
||||
{
|
||||
username: "foo",
|
||||
acct: "@foo",
|
||||
host: null,
|
||||
},
|
||||
{
|
||||
username: "bar",
|
||||
acct: "@bar",
|
||||
host: null,
|
||||
},
|
||||
{
|
||||
username: "baz",
|
||||
acct: "@baz",
|
||||
host: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("nested", () => {
|
||||
const ast = parse("@foo **@bar** @baz")!;
|
||||
const mentions = extractMentions(ast);
|
||||
assert.deepStrictEqual(mentions, [
|
||||
{
|
||||
username: "foo",
|
||||
acct: "@foo",
|
||||
host: null,
|
||||
},
|
||||
{
|
||||
username: "bar",
|
||||
acct: "@bar",
|
||||
host: null,
|
||||
},
|
||||
{
|
||||
username: "baz",
|
||||
acct: "@baz",
|
||||
host: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -1,213 +0,0 @@
|
|||
process.env.NODE_ENV = "test";
|
||||
|
||||
import * as assert from "node:assert";
|
||||
import type * as childProcess from "node:child_process";
|
||||
import * as openapi from "@redocly/openapi-core";
|
||||
import {
|
||||
async,
|
||||
port,
|
||||
post,
|
||||
request,
|
||||
shutdownServer,
|
||||
signup,
|
||||
simpleGet,
|
||||
startServer,
|
||||
} from "./utils.js";
|
||||
|
||||
// Request Accept
|
||||
const ONLY_AP = "application/activity+json";
|
||||
const PREFER_AP = "application/activity+json, */*";
|
||||
const PREFER_HTML = "text/html, */*";
|
||||
const UNSPECIFIED = "*/*";
|
||||
|
||||
// Response Contet-Type
|
||||
const AP = "application/activity+json; charset=utf-8";
|
||||
const JSON = "application/json; charset=utf-8";
|
||||
const HTML = "text/html; charset=utf-8";
|
||||
|
||||
describe("Fetch resource", () => {
|
||||
let p: childProcess.ChildProcess;
|
||||
|
||||
let alice: any;
|
||||
let alicesPost: any;
|
||||
|
||||
before(async () => {
|
||||
p = await startServer();
|
||||
alice = await signup({ username: "alice" });
|
||||
alicesPost = await post(alice, {
|
||||
text: "test",
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await shutdownServer(p);
|
||||
});
|
||||
|
||||
describe("Common", () => {
|
||||
it("meta", async(async () => {
|
||||
const res = await request("/meta", {});
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
}));
|
||||
|
||||
it("GET root", async(async () => {
|
||||
const res = await simpleGet("/");
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, HTML);
|
||||
}));
|
||||
|
||||
it("GET docs", async(async () => {
|
||||
const res = await simpleGet("/docs/ja-JP/about");
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, HTML);
|
||||
}));
|
||||
|
||||
it("GET api-doc", async(async () => {
|
||||
const res = await simpleGet("/api-doc");
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, HTML);
|
||||
}));
|
||||
|
||||
it("GET api.json", async(async () => {
|
||||
const res = await simpleGet("/api.json");
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, JSON);
|
||||
}));
|
||||
|
||||
it("Validate api.json", async(async () => {
|
||||
const config = await openapi.loadConfig();
|
||||
const result = await openapi.bundle({
|
||||
config,
|
||||
ref: `http://localhost:${port}/api.json`,
|
||||
});
|
||||
|
||||
for (const problem of result.problems) {
|
||||
console.log(`${problem.message} - ${problem.location[0]?.pointer}`);
|
||||
}
|
||||
|
||||
assert.strictEqual(result.problems.length, 0);
|
||||
}));
|
||||
|
||||
it("GET favicon.ico", async(async () => {
|
||||
const res = await simpleGet("/favicon.ico");
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, "image/x-icon");
|
||||
}));
|
||||
|
||||
it("GET apple-touch-icon.png", async(async () => {
|
||||
const res = await simpleGet("/apple-touch-icon.png");
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, "image/png");
|
||||
}));
|
||||
|
||||
it("GET twemoji svg", async(async () => {
|
||||
const res = await simpleGet("/twemoji/2764.svg");
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, "image/svg+xml");
|
||||
}));
|
||||
|
||||
it("GET twemoji svg with hyphen", async(async () => {
|
||||
const res = await simpleGet("/twemoji/2764-fe0f-200d-1f525.svg");
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, "image/svg+xml");
|
||||
}));
|
||||
});
|
||||
|
||||
describe("/@:username", () => {
|
||||
it("Only AP => AP", async(async () => {
|
||||
const res = await simpleGet(`/@${alice.username}`, ONLY_AP);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, AP);
|
||||
}));
|
||||
|
||||
it("Prefer AP => AP", async(async () => {
|
||||
const res = await simpleGet(`/@${alice.username}`, PREFER_AP);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, AP);
|
||||
}));
|
||||
|
||||
it("Prefer HTML => HTML", async(async () => {
|
||||
const res = await simpleGet(`/@${alice.username}`, PREFER_HTML);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, HTML);
|
||||
}));
|
||||
|
||||
it("Unspecified => HTML", async(async () => {
|
||||
const res = await simpleGet(`/@${alice.username}`, UNSPECIFIED);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, HTML);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("/users/:id", () => {
|
||||
it("Only AP => AP", async(async () => {
|
||||
const res = await simpleGet(`/users/${alice.id}`, ONLY_AP);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, AP);
|
||||
}));
|
||||
|
||||
it("Prefer AP => AP", async(async () => {
|
||||
const res = await simpleGet(`/users/${alice.id}`, PREFER_AP);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, AP);
|
||||
}));
|
||||
|
||||
it("Prefer HTML => Redirect to /@:username", async(async () => {
|
||||
const res = await simpleGet(`/users/${alice.id}`, PREFER_HTML);
|
||||
assert.strictEqual(res.status, 302);
|
||||
assert.strictEqual(res.location, `/@${alice.username}`);
|
||||
}));
|
||||
|
||||
it("Undecided => HTML", async(async () => {
|
||||
const res = await simpleGet(`/users/${alice.id}`, UNSPECIFIED);
|
||||
assert.strictEqual(res.status, 302);
|
||||
assert.strictEqual(res.location, `/@${alice.username}`);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("/notes/:id", () => {
|
||||
it("Only AP => AP", async(async () => {
|
||||
const res = await simpleGet(`/notes/${alicesPost.id}`, ONLY_AP);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, AP);
|
||||
}));
|
||||
|
||||
it("Prefer AP => AP", async(async () => {
|
||||
const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_AP);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, AP);
|
||||
}));
|
||||
|
||||
it("Prefer HTML => HTML", async(async () => {
|
||||
const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_HTML);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, HTML);
|
||||
}));
|
||||
|
||||
it("Unspecified => HTML", async(async () => {
|
||||
const res = await simpleGet(`/notes/${alicesPost.id}`, UNSPECIFIED);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, HTML);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("Feeds", () => {
|
||||
it("RSS", async(async () => {
|
||||
const res = await simpleGet(`/@${alice.username}.rss`, UNSPECIFIED);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, "application/rss+xml; charset=utf-8");
|
||||
}));
|
||||
|
||||
it("ATOM", async(async () => {
|
||||
const res = await simpleGet(`/@${alice.username}.atom`, UNSPECIFIED);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, "application/atom+xml; charset=utf-8");
|
||||
}));
|
||||
|
||||
it("JSON", async(async () => {
|
||||
const res = await simpleGet(`/@${alice.username}.json`, UNSPECIFIED);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, "application/json; charset=utf-8");
|
||||
}));
|
||||
});
|
||||
});
|
|
@ -1,283 +0,0 @@
|
|||
process.env.NODE_ENV = "test";
|
||||
|
||||
import * as assert from "node:assert";
|
||||
import type * as childProcess from "node:child_process";
|
||||
import {
|
||||
async,
|
||||
connectStream,
|
||||
post,
|
||||
react,
|
||||
request,
|
||||
shutdownServer,
|
||||
signup,
|
||||
simpleGet,
|
||||
startServer,
|
||||
} from "./utils.js";
|
||||
|
||||
describe("FF visibility", () => {
|
||||
let p: childProcess.ChildProcess;
|
||||
|
||||
let alice: any;
|
||||
let bob: any;
|
||||
let carol: any;
|
||||
|
||||
before(async () => {
|
||||
p = await startServer();
|
||||
alice = await signup({ username: "alice" });
|
||||
bob = await signup({ username: "bob" });
|
||||
carol = await signup({ username: "carol" });
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await shutdownServer(p);
|
||||
});
|
||||
|
||||
it("ffVisibility が public なユーザーのフォロー/フォロワーを誰でも見れる", async(async () => {
|
||||
await request(
|
||||
"/i/update",
|
||||
{
|
||||
ffVisibility: "public",
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
const followingRes = await request(
|
||||
"/users/following",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
bob,
|
||||
);
|
||||
const followersRes = await request(
|
||||
"/users/followers",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
bob,
|
||||
);
|
||||
|
||||
assert.strictEqual(followingRes.status, 200);
|
||||
assert.strictEqual(Array.isArray(followingRes.body), true);
|
||||
assert.strictEqual(followersRes.status, 200);
|
||||
assert.strictEqual(Array.isArray(followersRes.body), true);
|
||||
}));
|
||||
|
||||
it("ffVisibility が followers なユーザーのフォロー/フォロワーを自分で見れる", async(async () => {
|
||||
await request(
|
||||
"/i/update",
|
||||
{
|
||||
ffVisibility: "followers",
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
const followingRes = await request(
|
||||
"/users/following",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
const followersRes = await request(
|
||||
"/users/followers",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(followingRes.status, 200);
|
||||
assert.strictEqual(Array.isArray(followingRes.body), true);
|
||||
assert.strictEqual(followersRes.status, 200);
|
||||
assert.strictEqual(Array.isArray(followersRes.body), true);
|
||||
}));
|
||||
|
||||
it("ffVisibility が followers なユーザーのフォロー/フォロワーを非フォロワーが見れない", async(async () => {
|
||||
await request(
|
||||
"/i/update",
|
||||
{
|
||||
ffVisibility: "followers",
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
const followingRes = await request(
|
||||
"/users/following",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
bob,
|
||||
);
|
||||
const followersRes = await request(
|
||||
"/users/followers",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
bob,
|
||||
);
|
||||
|
||||
assert.strictEqual(followingRes.status, 400);
|
||||
assert.strictEqual(followersRes.status, 400);
|
||||
}));
|
||||
|
||||
it("ffVisibility が followers なユーザーのフォロー/フォロワーをフォロワーが見れる", async(async () => {
|
||||
await request(
|
||||
"/i/update",
|
||||
{
|
||||
ffVisibility: "followers",
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
await request(
|
||||
"/following/create",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
bob,
|
||||
);
|
||||
|
||||
const followingRes = await request(
|
||||
"/users/following",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
bob,
|
||||
);
|
||||
const followersRes = await request(
|
||||
"/users/followers",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
bob,
|
||||
);
|
||||
|
||||
assert.strictEqual(followingRes.status, 200);
|
||||
assert.strictEqual(Array.isArray(followingRes.body), true);
|
||||
assert.strictEqual(followersRes.status, 200);
|
||||
assert.strictEqual(Array.isArray(followersRes.body), true);
|
||||
}));
|
||||
|
||||
it("ffVisibility が private なユーザーのフォロー/フォロワーを自分で見れる", async(async () => {
|
||||
await request(
|
||||
"/i/update",
|
||||
{
|
||||
ffVisibility: "private",
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
const followingRes = await request(
|
||||
"/users/following",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
const followersRes = await request(
|
||||
"/users/followers",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(followingRes.status, 200);
|
||||
assert.strictEqual(Array.isArray(followingRes.body), true);
|
||||
assert.strictEqual(followersRes.status, 200);
|
||||
assert.strictEqual(Array.isArray(followersRes.body), true);
|
||||
}));
|
||||
|
||||
it("ffVisibility が private なユーザーのフォロー/フォロワーを他人が見れない", async(async () => {
|
||||
await request(
|
||||
"/i/update",
|
||||
{
|
||||
ffVisibility: "private",
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
const followingRes = await request(
|
||||
"/users/following",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
bob,
|
||||
);
|
||||
const followersRes = await request(
|
||||
"/users/followers",
|
||||
{
|
||||
userId: alice.id,
|
||||
},
|
||||
bob,
|
||||
);
|
||||
|
||||
assert.strictEqual(followingRes.status, 400);
|
||||
assert.strictEqual(followersRes.status, 400);
|
||||
}));
|
||||
|
||||
describe("AP", () => {
|
||||
it("ffVisibility が public 以外ならばAPからは取得できない", async(async () => {
|
||||
{
|
||||
await request(
|
||||
"/i/update",
|
||||
{
|
||||
ffVisibility: "public",
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
const followingRes = await simpleGet(
|
||||
`/users/${alice.id}/following`,
|
||||
"application/activity+json",
|
||||
);
|
||||
const followersRes = await simpleGet(
|
||||
`/users/${alice.id}/followers`,
|
||||
"application/activity+json",
|
||||
);
|
||||
assert.strictEqual(followingRes.status, 200);
|
||||
assert.strictEqual(followersRes.status, 200);
|
||||
}
|
||||
{
|
||||
await request(
|
||||
"/i/update",
|
||||
{
|
||||
ffVisibility: "followers",
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
const followingRes = await simpleGet(
|
||||
`/users/${alice.id}/following`,
|
||||
"application/activity+json",
|
||||
).catch((res) => ({ status: res.statusCode }));
|
||||
const followersRes = await simpleGet(
|
||||
`/users/${alice.id}/followers`,
|
||||
"application/activity+json",
|
||||
).catch((res) => ({ status: res.statusCode }));
|
||||
assert.strictEqual(followingRes.status, 403);
|
||||
assert.strictEqual(followersRes.status, 403);
|
||||
}
|
||||
{
|
||||
await request(
|
||||
"/i/update",
|
||||
{
|
||||
ffVisibility: "private",
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
const followingRes = await simpleGet(
|
||||
`/users/${alice.id}/following`,
|
||||
"application/activity+json",
|
||||
).catch((res) => ({ status: res.statusCode }));
|
||||
const followersRes = await simpleGet(
|
||||
`/users/${alice.id}/followers`,
|
||||
"application/activity+json",
|
||||
).catch((res) => ({ status: res.statusCode }));
|
||||
assert.strictEqual(followingRes.status, 403);
|
||||
assert.strictEqual(followersRes.status, 403);
|
||||
}
|
||||
}));
|
||||
});
|
||||
});
|
|
@ -1,209 +0,0 @@
|
|||
import * as assert from "node:assert";
|
||||
import { dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { getFileInfo } from "../src/misc/get-file-info.js";
|
||||
import { async } from "./utils.js";
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
describe("Get file info", () => {
|
||||
it("Empty file", async(async () => {
|
||||
const path = `${_dirname}/resources/emptyfile`;
|
||||
const info = (await getFileInfo(path, {
|
||||
skipSensitiveDetection: true,
|
||||
})) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 0,
|
||||
md5: "d41d8cd98f00b204e9800998ecf8427e",
|
||||
type: {
|
||||
mime: "application/octet-stream",
|
||||
ext: null,
|
||||
},
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
orientation: undefined,
|
||||
});
|
||||
}));
|
||||
|
||||
it("Generic JPEG", async(async () => {
|
||||
const path = `${_dirname}/resources/Lenna.jpg`;
|
||||
const info = (await getFileInfo(path, {
|
||||
skipSensitiveDetection: true,
|
||||
})) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 25360,
|
||||
md5: "091b3f259662aa31e2ffef4519951168",
|
||||
type: {
|
||||
mime: "image/jpeg",
|
||||
ext: "jpg",
|
||||
},
|
||||
width: 512,
|
||||
height: 512,
|
||||
orientation: undefined,
|
||||
});
|
||||
}));
|
||||
|
||||
it("Generic APNG", async(async () => {
|
||||
const path = `${_dirname}/resources/anime.png`;
|
||||
const info = (await getFileInfo(path, {
|
||||
skipSensitiveDetection: true,
|
||||
})) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 1868,
|
||||
md5: "08189c607bea3b952704676bb3c979e0",
|
||||
type: {
|
||||
mime: "image/apng",
|
||||
ext: "apng",
|
||||
},
|
||||
width: 256,
|
||||
height: 256,
|
||||
orientation: undefined,
|
||||
});
|
||||
}));
|
||||
|
||||
it("Generic AGIF", async(async () => {
|
||||
const path = `${_dirname}/resources/anime.gif`;
|
||||
const info = (await getFileInfo(path, {
|
||||
skipSensitiveDetection: true,
|
||||
})) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 2248,
|
||||
md5: "32c47a11555675d9267aee1a86571e7e",
|
||||
type: {
|
||||
mime: "image/gif",
|
||||
ext: "gif",
|
||||
},
|
||||
width: 256,
|
||||
height: 256,
|
||||
orientation: undefined,
|
||||
});
|
||||
}));
|
||||
|
||||
it("PNG with alpha", async(async () => {
|
||||
const path = `${_dirname}/resources/with-alpha.png`;
|
||||
const info = (await getFileInfo(path, {
|
||||
skipSensitiveDetection: true,
|
||||
})) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 3772,
|
||||
md5: "f73535c3e1e27508885b69b10cf6e991",
|
||||
type: {
|
||||
mime: "image/png",
|
||||
ext: "png",
|
||||
},
|
||||
width: 256,
|
||||
height: 256,
|
||||
orientation: undefined,
|
||||
});
|
||||
}));
|
||||
|
||||
it("Generic SVG", async(async () => {
|
||||
const path = `${_dirname}/resources/image.svg`;
|
||||
const info = (await getFileInfo(path, {
|
||||
skipSensitiveDetection: true,
|
||||
})) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 505,
|
||||
md5: "b6f52b4b021e7b92cdd04509c7267965",
|
||||
type: {
|
||||
mime: "image/svg+xml",
|
||||
ext: "svg",
|
||||
},
|
||||
width: 256,
|
||||
height: 256,
|
||||
orientation: undefined,
|
||||
});
|
||||
}));
|
||||
|
||||
it("SVG with XML definition", async(async () => {
|
||||
// https://github.com/misskey-dev/misskey/issues/4413
|
||||
const path = `${_dirname}/resources/with-xml-def.svg`;
|
||||
const info = (await getFileInfo(path, {
|
||||
skipSensitiveDetection: true,
|
||||
})) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 544,
|
||||
md5: "4b7a346cde9ccbeb267e812567e33397",
|
||||
type: {
|
||||
mime: "image/svg+xml",
|
||||
ext: "svg",
|
||||
},
|
||||
width: 256,
|
||||
height: 256,
|
||||
orientation: undefined,
|
||||
});
|
||||
}));
|
||||
|
||||
it("Dimension limit", async(async () => {
|
||||
const path = `${_dirname}/resources/25000x25000.png`;
|
||||
const info = (await getFileInfo(path, {
|
||||
skipSensitiveDetection: true,
|
||||
})) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 75933,
|
||||
md5: "268c5dde99e17cf8fe09f1ab3f97df56",
|
||||
type: {
|
||||
mime: "application/octet-stream", // do not treat as image
|
||||
ext: null,
|
||||
},
|
||||
width: 25000,
|
||||
height: 25000,
|
||||
orientation: undefined,
|
||||
});
|
||||
}));
|
||||
|
||||
it("Rotate JPEG", async(async () => {
|
||||
const path = `${_dirname}/resources/rotate.jpg`;
|
||||
const info = (await getFileInfo(path, {
|
||||
skipSensitiveDetection: true,
|
||||
})) as any;
|
||||
delete info.warnings;
|
||||
delete info.blurhash;
|
||||
delete info.sensitive;
|
||||
delete info.porn;
|
||||
assert.deepStrictEqual(info, {
|
||||
size: 12624,
|
||||
md5: "68d5b2d8d1d1acbbce99203e3ec3857e",
|
||||
type: {
|
||||
mime: "image/jpeg",
|
||||
ext: "jpg",
|
||||
},
|
||||
width: 512,
|
||||
height: 256,
|
||||
orientation: 8,
|
||||
});
|
||||
}));
|
||||
});
|
|
@ -1,37 +0,0 @@
|
|||
/**
|
||||
* ts-node/esmローダーに投げる前にpath mappingを解決する
|
||||
* 参考
|
||||
* - https://github.com/TypeStrong/ts-node/discussions/1450#discussioncomment-1806115
|
||||
* - https://nodejs.org/api/esm.html#loaders
|
||||
* ※ https://github.com/TypeStrong/ts-node/pull/1585 が取り込まれたらこのカスタムローダーは必要なくなる
|
||||
*/
|
||||
|
||||
import { resolve as resolveTs, load } from "ts-node/esm";
|
||||
import { loadConfig, createMatchPath } from "tsconfig-paths";
|
||||
import { pathToFileURL } from "url";
|
||||
|
||||
const tsconfig = loadConfig();
|
||||
const matchPath = createMatchPath(tsconfig.absoluteBaseUrl, tsconfig.paths);
|
||||
|
||||
export function resolve(specifier, ctx, defaultResolve) {
|
||||
let resolvedSpecifier;
|
||||
if (specifier.endsWith(".js")) {
|
||||
// maybe transpiled
|
||||
const specifierWithoutExtension = specifier.substring(
|
||||
0,
|
||||
specifier.length - ".js".length,
|
||||
);
|
||||
const matchedSpecifier = matchPath(specifierWithoutExtension);
|
||||
if (matchedSpecifier) {
|
||||
resolvedSpecifier = pathToFileURL(`${matchedSpecifier}.js`).href;
|
||||
}
|
||||
} else {
|
||||
const matchedSpecifier = matchPath(specifier);
|
||||
if (matchedSpecifier) {
|
||||
resolvedSpecifier = pathToFileURL(matchedSpecifier).href;
|
||||
}
|
||||
}
|
||||
return resolveTs(resolvedSpecifier ?? specifier, ctx, defaultResolve);
|
||||
}
|
||||
|
||||
export { load };
|
|
@ -1,127 +0,0 @@
|
|||
import * as assert from "node:assert";
|
||||
import * as mfm from "mfm-js";
|
||||
|
||||
import { fromHtml } from "../src/mfm/from-html.js";
|
||||
import { toHtml } from "../src/mfm/to-html.js";
|
||||
|
||||
describe("toHtml", () => {
|
||||
it("br", () => {
|
||||
const input = "foo\nbar\nbaz";
|
||||
const output = "<p><span>foo<br>bar<br>baz</span></p>";
|
||||
assert.equal(toHtml(mfm.parse(input)), output);
|
||||
});
|
||||
|
||||
it("br alt", () => {
|
||||
const input = "foo\r\nbar\rbaz";
|
||||
const output = "<p><span>foo<br>bar<br>baz</span></p>";
|
||||
assert.equal(toHtml(mfm.parse(input)), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe("fromHtml", () => {
|
||||
it("p", () => {
|
||||
assert.deepStrictEqual(fromHtml("<p>a</p><p>b</p>"), "a\n\nb");
|
||||
});
|
||||
|
||||
it("block element", () => {
|
||||
assert.deepStrictEqual(fromHtml("<div>a</div><div>b</div>"), "a\nb");
|
||||
});
|
||||
|
||||
it("inline element", () => {
|
||||
assert.deepStrictEqual(fromHtml("<ul><li>a</li><li>b</li></ul>"), "a\nb");
|
||||
});
|
||||
|
||||
it("block code", () => {
|
||||
assert.deepStrictEqual(
|
||||
fromHtml("<pre><code>a\nb</code></pre>"),
|
||||
"```\na\nb\n```",
|
||||
);
|
||||
});
|
||||
|
||||
it("inline code", () => {
|
||||
assert.deepStrictEqual(fromHtml("<code>a</code>"), "`a`");
|
||||
});
|
||||
|
||||
it("quote", () => {
|
||||
assert.deepStrictEqual(
|
||||
fromHtml("<blockquote>a\nb</blockquote>"),
|
||||
"> a\n> b",
|
||||
);
|
||||
});
|
||||
|
||||
it("br", () => {
|
||||
assert.deepStrictEqual(fromHtml("<p>abc<br><br/>d</p>"), "abc\n\nd");
|
||||
});
|
||||
|
||||
it("link with different text", () => {
|
||||
assert.deepStrictEqual(
|
||||
fromHtml('<p>a <a href="https://firefish.dev/firefish">c</a> d</p>'),
|
||||
"a [c](https://firefish.dev/firefish) d",
|
||||
);
|
||||
});
|
||||
|
||||
it("link with different text, but not encoded", () => {
|
||||
assert.deepStrictEqual(
|
||||
fromHtml('<p>a <a href="https://firefish.dev/ä">c</a> d</p>'),
|
||||
"a [c](<https://firefish.dev/ä>) d",
|
||||
);
|
||||
});
|
||||
|
||||
it("link with same text", () => {
|
||||
assert.deepStrictEqual(
|
||||
fromHtml(
|
||||
'<p>a <a href="https://firefish.dev/firefish/firefish">https://firefish.dev/firefish/firefish</a> d</p>',
|
||||
),
|
||||
"a https://firefish.dev/firefish/firefish d",
|
||||
);
|
||||
});
|
||||
|
||||
it("link with same text, but not encoded", () => {
|
||||
assert.deepStrictEqual(
|
||||
fromHtml(
|
||||
'<p>a <a href="https://firefish.dev/ä">https://firefish.dev/ä</a> d</p>',
|
||||
),
|
||||
"a <https://firefish.dev/ä> d",
|
||||
);
|
||||
});
|
||||
|
||||
it("link with no url", () => {
|
||||
assert.deepStrictEqual(
|
||||
fromHtml('<p>a <a href="b">c</a> d</p>'),
|
||||
"a [c](b) d",
|
||||
);
|
||||
});
|
||||
|
||||
it("link without href", () => {
|
||||
assert.deepStrictEqual(fromHtml("<p>a <a>c</a> d</p>"), "a c d");
|
||||
});
|
||||
|
||||
it("link without text", () => {
|
||||
assert.deepStrictEqual(
|
||||
fromHtml('<p>a <a href="https://firefish.dev/b"></a> d</p>'),
|
||||
"a https://firefish.dev/b d",
|
||||
);
|
||||
});
|
||||
|
||||
it("link without both", () => {
|
||||
assert.deepStrictEqual(fromHtml("<p>a <a></a> d</p>"), "a d");
|
||||
});
|
||||
|
||||
it("mention", () => {
|
||||
assert.deepStrictEqual(
|
||||
fromHtml(
|
||||
'<p>a <a href="https://info.firefish.dev/@firefish" class="u-url mention">@firefish</a> d</p>',
|
||||
),
|
||||
"a @firefish@info.firefish.dev d",
|
||||
);
|
||||
});
|
||||
|
||||
it("hashtag", () => {
|
||||
assert.deepStrictEqual(
|
||||
fromHtml('<p>a <a href="https://info.firefish.dev/tags/a">#a</a> d</p>', [
|
||||
"#a",
|
||||
]),
|
||||
"a #a d",
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,39 +0,0 @@
|
|||
import Resolver from "../../src/remote/activitypub/resolver.js";
|
||||
import { IObject } from "../../src/remote/activitypub/type.js";
|
||||
|
||||
type MockResponse = {
|
||||
type: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
export class MockResolver extends Resolver {
|
||||
private _rs = new Map<string, MockResponse>();
|
||||
public async _register(
|
||||
uri: string,
|
||||
content: string | Record<string, any>,
|
||||
type = "application/activity+json",
|
||||
) {
|
||||
this._rs.set(uri, {
|
||||
type,
|
||||
content: typeof content === "string" ? content : JSON.stringify(content),
|
||||
});
|
||||
}
|
||||
|
||||
public async resolve(value: string | IObject): Promise<IObject> {
|
||||
if (typeof value !== "string") return value;
|
||||
|
||||
const r = this._rs.get(value);
|
||||
|
||||
if (!r) {
|
||||
throw {
|
||||
name: "StatusError",
|
||||
statusCode: 404,
|
||||
message: "Not registed for mock",
|
||||
};
|
||||
}
|
||||
|
||||
const object = JSON.parse(r.content);
|
||||
|
||||
return object;
|
||||
}
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
process.env.NODE_ENV = "test";
|
||||
|
||||
import * as assert from "node:assert";
|
||||
import type * as childProcess from "node:child_process";
|
||||
import {
|
||||
async,
|
||||
post,
|
||||
react,
|
||||
request,
|
||||
shutdownServer,
|
||||
signup,
|
||||
startServer,
|
||||
waitFire,
|
||||
} from "./utils.js";
|
||||
|
||||
describe("Mute", () => {
|
||||
let p: childProcess.ChildProcess;
|
||||
|
||||
// alice mutes carol
|
||||
let alice: any;
|
||||
let bob: any;
|
||||
let carol: any;
|
||||
|
||||
before(async () => {
|
||||
p = await startServer();
|
||||
alice = await signup({ username: "alice" });
|
||||
bob = await signup({ username: "bob" });
|
||||
carol = await signup({ username: "carol" });
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await shutdownServer(p);
|
||||
});
|
||||
|
||||
it("ミュート作成", async(async () => {
|
||||
const res = await request(
|
||||
"/mute/create",
|
||||
{
|
||||
userId: carol.id,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 204);
|
||||
}));
|
||||
|
||||
it("「自分宛ての投稿」にミュートしているユーザーの投稿が含まれない", async(async () => {
|
||||
const bobNote = await post(bob, { text: "@alice hi" });
|
||||
const carolNote = await post(carol, { text: "@alice hi" });
|
||||
|
||||
const res = await request("/notes/mentions", {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === bobNote.id),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === carolNote.id),
|
||||
false,
|
||||
);
|
||||
}));
|
||||
|
||||
it("ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない", async(async () => {
|
||||
// 状態リセット
|
||||
await request("/i/read-all-unread-notes", {}, alice);
|
||||
|
||||
await post(carol, { text: "@alice hi" });
|
||||
|
||||
const res = await request("/i", {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.body.hasUnreadMentions, false);
|
||||
}));
|
||||
|
||||
it("ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない", async () => {
|
||||
// 状態リセット
|
||||
await request("/i/read-all-unread-notes", {}, alice);
|
||||
|
||||
const fired = await waitFire(
|
||||
alice,
|
||||
"main",
|
||||
() => post(carol, { text: "@alice hi" }),
|
||||
(msg) => msg.type === "unreadMention",
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
it("ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない", async () => {
|
||||
// 状態リセット
|
||||
await request("/i/read-all-unread-notes", {}, alice);
|
||||
await request("/notifications/mark-all-as-read", {}, alice);
|
||||
|
||||
const fired = await waitFire(
|
||||
alice,
|
||||
"main",
|
||||
() => post(carol, { text: "@alice hi" }),
|
||||
(msg) => msg.type === "unreadNotification",
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
describe("Timeline", () => {
|
||||
it("タイムラインにミュートしているユーザーの投稿が含まれない", async(async () => {
|
||||
const aliceNote = await post(alice);
|
||||
const bobNote = await post(bob);
|
||||
const carolNote = await post(carol);
|
||||
|
||||
const res = await request("/notes/local-timeline", {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === aliceNote.id),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === bobNote.id),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === carolNote.id),
|
||||
false,
|
||||
);
|
||||
}));
|
||||
|
||||
it("タイムラインにミュートしているユーザーの投稿のRenoteが含まれない", async(async () => {
|
||||
const aliceNote = await post(alice);
|
||||
const carolNote = await post(carol);
|
||||
const bobNote = await post(bob, {
|
||||
renoteId: carolNote.id,
|
||||
});
|
||||
|
||||
const res = await request("/notes/local-timeline", {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === aliceNote.id),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === bobNote.id),
|
||||
false,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === carolNote.id),
|
||||
false,
|
||||
);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("Notification", () => {
|
||||
it("通知にミュートしているユーザーの通知が含まれない(リアクション)", async(async () => {
|
||||
const aliceNote = await post(alice);
|
||||
await react(bob, aliceNote, "like");
|
||||
await react(carol, aliceNote, "like");
|
||||
|
||||
const res = await request("/i/notifications", {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(
|
||||
res.body.some((notification: any) => notification.userId === bob.id),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((notification: any) => notification.userId === carol.id),
|
||||
false,
|
||||
);
|
||||
}));
|
||||
});
|
||||
});
|
|
@ -1,517 +0,0 @@
|
|||
process.env.NODE_ENV = "test";
|
||||
|
||||
import * as assert from "node:assert";
|
||||
import type * as childProcess from "node:child_process";
|
||||
import { Note } from "../src/models/entities/note.js";
|
||||
import {
|
||||
api,
|
||||
async,
|
||||
initTestDb,
|
||||
post,
|
||||
request,
|
||||
shutdownServer,
|
||||
signup,
|
||||
startServer,
|
||||
uploadUrl,
|
||||
} from "./utils.js";
|
||||
|
||||
describe("Note", () => {
|
||||
let p: childProcess.ChildProcess;
|
||||
let Notes: any;
|
||||
|
||||
let alice: any;
|
||||
let bob: any;
|
||||
|
||||
before(async () => {
|
||||
p = await startServer();
|
||||
const connection = await initTestDb(true);
|
||||
Notes = connection.getRepository(Note);
|
||||
alice = await signup({ username: "alice" });
|
||||
bob = await signup({ username: "bob" });
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await shutdownServer(p);
|
||||
});
|
||||
|
||||
it("投稿できる", async(async () => {
|
||||
const post = {
|
||||
text: "test",
|
||||
};
|
||||
|
||||
const res = await request("/notes/create", post, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(
|
||||
typeof res.body === "object" && !Array.isArray(res.body),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(res.body.createdNote.text, post.text);
|
||||
}));
|
||||
|
||||
it("ファイルを添付できる", async(async () => {
|
||||
const file = await uploadUrl(
|
||||
alice,
|
||||
"https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg",
|
||||
);
|
||||
|
||||
const res = await request(
|
||||
"/notes/create",
|
||||
{
|
||||
fileIds: [file.id],
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(
|
||||
typeof res.body === "object" && !Array.isArray(res.body),
|
||||
true,
|
||||
);
|
||||
assert.deepStrictEqual(res.body.createdNote.fileIds, [file.id]);
|
||||
}));
|
||||
|
||||
it("他人のファイルは無視", async(async () => {
|
||||
const file = await uploadUrl(
|
||||
bob,
|
||||
"https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg",
|
||||
);
|
||||
|
||||
const res = await request(
|
||||
"/notes/create",
|
||||
{
|
||||
text: "test",
|
||||
fileIds: [file.id],
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(
|
||||
typeof res.body === "object" && !Array.isArray(res.body),
|
||||
true,
|
||||
);
|
||||
assert.deepStrictEqual(res.body.createdNote.fileIds, []);
|
||||
}));
|
||||
|
||||
it("存在しないファイルは無視", async(async () => {
|
||||
const res = await request(
|
||||
"/notes/create",
|
||||
{
|
||||
text: "test",
|
||||
fileIds: ["000000000000000000000000"],
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(
|
||||
typeof res.body === "object" && !Array.isArray(res.body),
|
||||
true,
|
||||
);
|
||||
assert.deepStrictEqual(res.body.createdNote.fileIds, []);
|
||||
}));
|
||||
|
||||
it("不正なファイルIDは無視", async(async () => {
|
||||
const res = await request(
|
||||
"/notes/create",
|
||||
{
|
||||
fileIds: ["kyoppie"],
|
||||
},
|
||||
alice,
|
||||
);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(
|
||||
typeof res.body === "object" && !Array.isArray(res.body),
|
||||
true,
|
||||
);
|
||||
assert.deepStrictEqual(res.body.createdNote.fileIds, []);
|
||||
}));
|
||||
|
||||
it("返信できる", async(async () => {
|
||||
const bobPost = await post(bob, {
|
||||
text: "foo",
|
||||
});
|
||||
|
||||
const alicePost = {
|
||||
text: "bar",
|
||||
replyId: bobPost.id,
|
||||
};
|
||||
|
||||
const res = await request("/notes/create", alicePost, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(
|
||||
typeof res.body === "object" && !Array.isArray(res.body),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(res.body.createdNote.text, alicePost.text);
|
||||
assert.strictEqual(res.body.createdNote.replyId, alicePost.replyId);
|
||||
assert.strictEqual(res.body.createdNote.reply.text, bobPost.text);
|
||||
}));
|
||||
|
||||
it("renoteできる", async(async () => {
|
||||
const bobPost = await post(bob, {
|
||||
text: "test",
|
||||
});
|
||||
|
||||
const alicePost = {
|
||||
renoteId: bobPost.id,
|
||||
};
|
||||
|
||||
const res = await request("/notes/create", alicePost, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(
|
||||
typeof res.body === "object" && !Array.isArray(res.body),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(res.body.createdNote.renoteId, alicePost.renoteId);
|
||||
assert.strictEqual(res.body.createdNote.renote.text, bobPost.text);
|
||||
}));
|
||||
|
||||
it("引用renoteできる", async(async () => {
|
||||
const bobPost = await post(bob, {
|
||||
text: "test",
|
||||
});
|
||||
|
||||
const alicePost = {
|
||||
text: "test",
|
||||
renoteId: bobPost.id,
|
||||
};
|
||||
|
||||
const res = await request("/notes/create", alicePost, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(
|
||||
typeof res.body === "object" && !Array.isArray(res.body),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(res.body.createdNote.text, alicePost.text);
|
||||
assert.strictEqual(res.body.createdNote.renoteId, alicePost.renoteId);
|
||||
assert.strictEqual(res.body.createdNote.renote.text, bobPost.text);
|
||||
}));
|
||||
|
||||
it("文字数ぎりぎりで怒られない", async(async () => {
|
||||
const post = {
|
||||
text: "!".repeat(3000),
|
||||
};
|
||||
const res = await request("/notes/create", post, alice);
|
||||
assert.strictEqual(res.status, 200);
|
||||
}));
|
||||
|
||||
it("文字数オーバーで怒られる", async(async () => {
|
||||
const post = {
|
||||
text: "!".repeat(3001),
|
||||
};
|
||||
const res = await request("/notes/create", post, alice);
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it("存在しないリプライ先で怒られる", async(async () => {
|
||||
const post = {
|
||||
text: "test",
|
||||
replyId: "000000000000000000000000",
|
||||
};
|
||||
const res = await request("/notes/create", post, alice);
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it("存在しないrenote対象で怒られる", async(async () => {
|
||||
const post = {
|
||||
renoteId: "000000000000000000000000",
|
||||
};
|
||||
const res = await request("/notes/create", post, alice);
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it("不正なリプライ先IDで怒られる", async(async () => {
|
||||
const post = {
|
||||
text: "test",
|
||||
replyId: "foo",
|
||||
};
|
||||
const res = await request("/notes/create", post, alice);
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it("不正なrenote対象IDで怒られる", async(async () => {
|
||||
const post = {
|
||||
renoteId: "foo",
|
||||
};
|
||||
const res = await request("/notes/create", post, alice);
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it("存在しないユーザーにメンションできる", async(async () => {
|
||||
const post = {
|
||||
text: "@ghost yo",
|
||||
};
|
||||
|
||||
const res = await request("/notes/create", post, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(
|
||||
typeof res.body === "object" && !Array.isArray(res.body),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(res.body.createdNote.text, post.text);
|
||||
}));
|
||||
|
||||
it("同じユーザーに複数メンションしても内部的にまとめられる", async(async () => {
|
||||
const post = {
|
||||
text: "@bob @bob @bob yo",
|
||||
};
|
||||
|
||||
const res = await request("/notes/create", post, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(
|
||||
typeof res.body === "object" && !Array.isArray(res.body),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(res.body.createdNote.text, post.text);
|
||||
|
||||
const noteDoc = await Notes.findOneBy({ id: res.body.createdNote.id });
|
||||
assert.deepStrictEqual(noteDoc.mentions, [bob.id]);
|
||||
}));
|
||||
|
||||
describe("notes/create", () => {
|
||||
it("投票を添付できる", async(async () => {
|
||||
const res = await request(
|
||||
"/notes/create",
|
||||
{
|
||||
text: "test",
|
||||
poll: {
|
||||
choices: ["foo", "bar"],
|
||||
},
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(
|
||||
typeof res.body === "object" && !Array.isArray(res.body),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(res.body.createdNote.poll != null, true);
|
||||
}));
|
||||
|
||||
it("投票の選択肢が無くて怒られる", async(async () => {
|
||||
const res = await request(
|
||||
"/notes/create",
|
||||
{
|
||||
poll: {},
|
||||
},
|
||||
alice,
|
||||
);
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it("投票の選択肢が無くて怒られる (空の配列)", async(async () => {
|
||||
const res = await request(
|
||||
"/notes/create",
|
||||
{
|
||||
poll: {
|
||||
choices: [],
|
||||
},
|
||||
},
|
||||
alice,
|
||||
);
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it("投票の選択肢が1つで怒られる", async(async () => {
|
||||
const res = await request(
|
||||
"/notes/create",
|
||||
{
|
||||
poll: {
|
||||
choices: ["Strawberry Pasta"],
|
||||
},
|
||||
},
|
||||
alice,
|
||||
);
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it("投票できる", async(async () => {
|
||||
const { body } = await request(
|
||||
"/notes/create",
|
||||
{
|
||||
text: "test",
|
||||
poll: {
|
||||
choices: ["sakura", "izumi", "ako"],
|
||||
},
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
const res = await request(
|
||||
"/notes/polls/vote",
|
||||
{
|
||||
noteId: body.createdNote.id,
|
||||
choice: 1,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 204);
|
||||
}));
|
||||
|
||||
it("複数投票できない", async(async () => {
|
||||
const { body } = await request(
|
||||
"/notes/create",
|
||||
{
|
||||
text: "test",
|
||||
poll: {
|
||||
choices: ["sakura", "izumi", "ako"],
|
||||
},
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
await request(
|
||||
"/notes/polls/vote",
|
||||
{
|
||||
noteId: body.createdNote.id,
|
||||
choice: 0,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
const res = await request(
|
||||
"/notes/polls/vote",
|
||||
{
|
||||
noteId: body.createdNote.id,
|
||||
choice: 2,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
|
||||
it("許可されている場合は複数投票できる", async(async () => {
|
||||
const { body } = await request(
|
||||
"/notes/create",
|
||||
{
|
||||
text: "test",
|
||||
poll: {
|
||||
choices: ["sakura", "izumi", "ako"],
|
||||
multiple: true,
|
||||
},
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
await request(
|
||||
"/notes/polls/vote",
|
||||
{
|
||||
noteId: body.createdNote.id,
|
||||
choice: 0,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
await request(
|
||||
"/notes/polls/vote",
|
||||
{
|
||||
noteId: body.createdNote.id,
|
||||
choice: 1,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
const res = await request(
|
||||
"/notes/polls/vote",
|
||||
{
|
||||
noteId: body.createdNote.id,
|
||||
choice: 2,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 204);
|
||||
}));
|
||||
|
||||
it("締め切られている場合は投票できない", async(async () => {
|
||||
const { body } = await request(
|
||||
"/notes/create",
|
||||
{
|
||||
text: "test",
|
||||
poll: {
|
||||
choices: ["sakura", "izumi", "ako"],
|
||||
expiredAfter: 1,
|
||||
},
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
await new Promise((x) => setTimeout(x, 2));
|
||||
|
||||
const res = await request(
|
||||
"/notes/polls/vote",
|
||||
{
|
||||
noteId: body.createdNote.id,
|
||||
choice: 1,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 400);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("notes/delete", () => {
|
||||
it("delete a reply", async(async () => {
|
||||
const mainNoteRes = await api(
|
||||
"notes/create",
|
||||
{
|
||||
text: "main post",
|
||||
},
|
||||
alice,
|
||||
);
|
||||
const replyOneRes = await api(
|
||||
"notes/create",
|
||||
{
|
||||
text: "reply one",
|
||||
replyId: mainNoteRes.body.createdNote.id,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
const replyTwoRes = await api(
|
||||
"notes/create",
|
||||
{
|
||||
text: "reply two",
|
||||
replyId: mainNoteRes.body.createdNote.id,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
const deleteOneRes = await api(
|
||||
"notes/delete",
|
||||
{
|
||||
noteId: replyOneRes.body.createdNote.id,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(deleteOneRes.status, 204);
|
||||
let mainNote = await Notes.findOneBy({
|
||||
id: mainNoteRes.body.createdNote.id,
|
||||
});
|
||||
assert.strictEqual(mainNote.repliesCount, 1);
|
||||
|
||||
const deleteTwoRes = await api(
|
||||
"notes/delete",
|
||||
{
|
||||
noteId: replyTwoRes.body.createdNote.id,
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(deleteTwoRes.status, 204);
|
||||
mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id });
|
||||
assert.strictEqual(mainNote.repliesCount, 0);
|
||||
}));
|
||||
});
|
||||
});
|
|
@ -1,18 +0,0 @@
|
|||
import * as assert from "node:assert";
|
||||
import { just, nothing } from "../../src/prelude/maybe.js";
|
||||
|
||||
describe("just", () => {
|
||||
it("has a value", () => {
|
||||
assert.deepStrictEqual(just(3).isJust(), true);
|
||||
});
|
||||
|
||||
it("has the inverse called get", () => {
|
||||
assert.deepStrictEqual(just(3).get(), 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe("nothing", () => {
|
||||
it("has no value", () => {
|
||||
assert.deepStrictEqual(nothing().isJust(), false);
|
||||
});
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
import * as assert from "node:assert";
|
||||
import { query } from "../../src/prelude/url.js";
|
||||
|
||||
describe("url", () => {
|
||||
it("query", () => {
|
||||
const s = query({
|
||||
foo: "ふぅ",
|
||||
bar: "b a r",
|
||||
baz: undefined,
|
||||
});
|
||||
assert.deepStrictEqual(s, "foo=%E3%81%B5%E3%81%85&bar=b%20a%20r");
|
||||
});
|
||||
});
|
Before Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 463 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.8 KiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path fill="#FF40A4" d="M128 80c-16 4-20 24-20 48v16c0 8-8 16-20.3 8 4.3 20 24.3 28 40.3 24s20-24 20-48v-16c0-8 8-16 20.3-8C164 84 144 76 128 80"/><path fill="#FFBF40" d="M192 80c-16 4-20 24-20 48v16c0 8-8 16-20.3 8 4.3 20 24.3 28 40.3 24s20-24 20-48v-16c0-8 8-16 20.3-8C228 84 208 76 192 80"/><path fill="#408EFF" d="M64 80c-16 4-20 24-20 48v16c0 8-8 16-20.3 8C28 172 48 180 64 176s20-24 20-48v-16c0-8 8-16 20.3-8C100 84 80 76 64 80"/></svg>
|
Before Width: | Height: | Size: 504 B |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 3.7 KiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path fill="#FF40A4" d="M128 80c-16 4-20 24-20 48v16c0 8-8 16-20.3 8 4.3 20 24.3 28 40.3 24s20-24 20-48v-16c0-8 8-16 20.3-8C164 84 144 76 128 80"/><path fill="#FFBF40" d="M192 80c-16 4-20 24-20 48v16c0 8-8 16-20.3 8 4.3 20 24.3 28 40.3 24s20-24 20-48v-16c0-8 8-16 20.3-8C228 84 208 76 192 80"/><path fill="#408EFF" d="M64 80c-16 4-20 24-20 48v16c0 8-8 16-20.3 8C28 172 48 180 64 176s20-24 20-48v-16c0-8 8-16 20.3-8C100 84 80 76 64 80"/></svg>
|
Before Width: | Height: | Size: 504 B |
|
@ -1,766 +0,0 @@
|
|||
process.env.NODE_ENV = "test";
|
||||
|
||||
import * as assert from "node:assert";
|
||||
import type * as childProcess from "node:child_process";
|
||||
import { Following } from "../src/models/entities/following.js";
|
||||
import {
|
||||
api,
|
||||
connectStream,
|
||||
initTestDb,
|
||||
post,
|
||||
shutdownServer,
|
||||
signup,
|
||||
startServer,
|
||||
waitFire,
|
||||
} from "./utils.js";
|
||||
|
||||
describe("Streaming", () => {
|
||||
let p: childProcess.ChildProcess;
|
||||
let Followings: any;
|
||||
|
||||
const follow = async (follower: any, followee: any) => {
|
||||
await Followings.save({
|
||||
id: "a",
|
||||
createdAt: new Date(),
|
||||
followerId: follower.id,
|
||||
followeeId: followee.id,
|
||||
followerHost: follower.host,
|
||||
followerInbox: null,
|
||||
followerSharedInbox: null,
|
||||
followeeHost: followee.host,
|
||||
followeeInbox: null,
|
||||
followeeSharedInbox: null,
|
||||
});
|
||||
};
|
||||
|
||||
describe("Streaming", () => {
|
||||
// Local users
|
||||
let ayano: any;
|
||||
let kyoko: any;
|
||||
let chitose: any;
|
||||
|
||||
// Remote users
|
||||
let akari: any;
|
||||
let chinatsu: any;
|
||||
|
||||
let kyokoNote: any;
|
||||
let list: any;
|
||||
|
||||
before(async () => {
|
||||
p = await startServer();
|
||||
const connection = await initTestDb(true);
|
||||
Followings = connection.getRepository(Following);
|
||||
|
||||
ayano = await signup({ username: "ayano" });
|
||||
kyoko = await signup({ username: "kyoko" });
|
||||
chitose = await signup({ username: "chitose" });
|
||||
|
||||
akari = await signup({ username: "akari", host: "example.com" });
|
||||
chinatsu = await signup({ username: "chinatsu", host: "example.com" });
|
||||
|
||||
kyokoNote = await post(kyoko, { text: "foo" });
|
||||
|
||||
// Follow: ayano => kyoko
|
||||
await api("following/create", { userId: kyoko.id }, ayano);
|
||||
|
||||
// Follow: ayano => akari
|
||||
await follow(ayano, akari);
|
||||
|
||||
// List: chitose => ayano, kyoko
|
||||
list = await api(
|
||||
"users/lists/create",
|
||||
{
|
||||
name: "my list",
|
||||
},
|
||||
chitose,
|
||||
).then((x) => x.body);
|
||||
|
||||
await api(
|
||||
"users/lists/push",
|
||||
{
|
||||
listId: list.id,
|
||||
userId: ayano.id,
|
||||
},
|
||||
chitose,
|
||||
);
|
||||
|
||||
await api(
|
||||
"users/lists/push",
|
||||
{
|
||||
listId: list.id,
|
||||
userId: kyoko.id,
|
||||
},
|
||||
chitose,
|
||||
);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await shutdownServer(p);
|
||||
});
|
||||
|
||||
describe("Events", () => {
|
||||
it("mention event", async () => {
|
||||
const fired = await waitFire(
|
||||
kyoko,
|
||||
"main", // kyoko:main
|
||||
() => post(ayano, { text: "foo @kyoko bar" }), // ayano mention => kyoko
|
||||
(msg) => msg.type === "mention" && msg.body.userId === ayano.id, // wait ayano
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("renote event", async () => {
|
||||
const fired = await waitFire(
|
||||
kyoko,
|
||||
"main", // kyoko:main
|
||||
() => post(ayano, { renoteId: kyokoNote.id }), // ayano renote
|
||||
(msg) => msg.type === "renote" && msg.body.renoteId === kyokoNote.id, // wait renote
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Home Timeline", () => {
|
||||
it("自分の投稿が流れる", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"homeTimeline", // ayano:Home
|
||||
() => api("notes/create", { text: "foo" }, ayano), // ayano posts
|
||||
(msg) => msg.type === "note" && msg.body.text === "foo",
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("フォローしているユーザーの投稿が流れる", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"homeTimeline", // ayano:home
|
||||
() => api("notes/create", { text: "foo" }, kyoko), // kyoko posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === kyoko.id, // wait kyoko
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("フォローしていないユーザーの投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
kyoko,
|
||||
"homeTimeline", // kyoko:home
|
||||
() => api("notes/create", { text: "foo" }, ayano), // ayano posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === ayano.id, // wait ayano
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
it("フォローしているユーザーのダイレクト投稿が流れる", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"homeTimeline", // ayano:home
|
||||
() =>
|
||||
api(
|
||||
"notes/create",
|
||||
{
|
||||
text: "foo",
|
||||
visibility: "specified",
|
||||
visibleUserIds: [ayano.id],
|
||||
},
|
||||
kyoko,
|
||||
), // kyoko dm => ayano
|
||||
(msg) => msg.type === "note" && msg.body.userId === kyoko.id, // wait kyoko
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("フォローしているユーザーでも自分が指定されていないダイレクト投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"homeTimeline", // ayano:home
|
||||
() =>
|
||||
api(
|
||||
"notes/create",
|
||||
{
|
||||
text: "foo",
|
||||
visibility: "specified",
|
||||
visibleUserIds: [chitose.id],
|
||||
},
|
||||
kyoko,
|
||||
), // kyoko dm => chitose
|
||||
(msg) => msg.type === "note" && msg.body.userId === kyoko.id, // wait kyoko
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
}); // Home
|
||||
|
||||
describe("Local Timeline", () => {
|
||||
it("自分の投稿が流れる", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"localTimeline", // ayano:Local
|
||||
() => api("notes/create", { text: "foo" }, ayano), // ayano posts
|
||||
(msg) => msg.type === "note" && msg.body.text === "foo",
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("フォローしていないローカルユーザーの投稿が流れる", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"localTimeline", // ayano:Local
|
||||
() => api("notes/create", { text: "foo" }, chitose), // chitose posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === chitose.id, // wait chitose
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("リモートユーザーの投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"localTimeline", // ayano:Local
|
||||
() => api("notes/create", { text: "foo" }, chinatsu), // chinatsu posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === chinatsu.id, // wait chinatsu
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
it("フォローしてたとしてもリモートユーザーの投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"localTimeline", // ayano:Local
|
||||
() => api("notes/create", { text: "foo" }, akari), // akari posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === akari.id, // wait akari
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
it("ホーム指定の投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"localTimeline", // ayano:Local
|
||||
() => api("notes/create", { text: "foo", visibility: "home" }, kyoko), // kyoko home posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === kyoko.id, // wait kyoko
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
it("フォローしているローカルユーザーのダイレクト投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"localTimeline", // ayano:Local
|
||||
() =>
|
||||
api(
|
||||
"notes/create",
|
||||
{
|
||||
text: "foo",
|
||||
visibility: "specified",
|
||||
visibleUserIds: [ayano.id],
|
||||
},
|
||||
kyoko,
|
||||
), // kyoko DM => ayano
|
||||
(msg) => msg.type === "note" && msg.body.userId === kyoko.id, // wait kyoko
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
it("フォローしていないローカルユーザーのフォロワー宛て投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"localTimeline", // ayano:Local
|
||||
() =>
|
||||
api(
|
||||
"notes/create",
|
||||
{ text: "foo", visibility: "followers" },
|
||||
chitose,
|
||||
),
|
||||
(msg) => msg.type === "note" && msg.body.userId === chitose.id, // wait chitose
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Recommended Timeline", () => {
|
||||
it("自分の投稿が流れる", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"recommendedTimeline", // ayano:Local
|
||||
() => api("notes/create", { text: "foo" }, ayano), // ayano posts
|
||||
(msg) => msg.type === "note" && msg.body.text === "foo",
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("フォローしていないローカルユーザーの投稿が流れる", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"recommendedTimeline", // ayano:Local
|
||||
() => api("notes/create", { text: "foo" }, chitose), // chitose posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === chitose.id, // wait chitose
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("リモートユーザーの投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"recommendedTimeline", // ayano:Local
|
||||
() => api("notes/create", { text: "foo" }, chinatsu), // chinatsu posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === chinatsu.id, // wait chinatsu
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
it("フォローしてたとしてもリモートユーザーの投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"recommendedTimeline", // ayano:Local
|
||||
() => api("notes/create", { text: "foo" }, akari), // akari posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === akari.id, // wait akari
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
it("ホーム指定の投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"recommendedTimeline", // ayano:Local
|
||||
() => api("notes/create", { text: "foo", visibility: "home" }, kyoko), // kyoko home posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === kyoko.id, // wait kyoko
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
it("フォローしているローカルユーザーのダイレクト投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"recommendedTimeline", // ayano:Local
|
||||
() =>
|
||||
api(
|
||||
"notes/create",
|
||||
{
|
||||
text: "foo",
|
||||
visibility: "specified",
|
||||
visibleUserIds: [ayano.id],
|
||||
},
|
||||
kyoko,
|
||||
), // kyoko DM => ayano
|
||||
(msg) => msg.type === "note" && msg.body.userId === kyoko.id, // wait kyoko
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
it("フォローしていないローカルユーザーのフォロワー宛て投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"recommendedTimeline", // ayano:Local
|
||||
() =>
|
||||
api(
|
||||
"notes/create",
|
||||
{ text: "foo", visibility: "followers" },
|
||||
chitose,
|
||||
),
|
||||
(msg) => msg.type === "note" && msg.body.userId === chitose.id, // wait chitose
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Hybrid Timeline", () => {
|
||||
it("自分の投稿が流れる", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"hybridTimeline", // ayano:Hybrid
|
||||
() => api("notes/create", { text: "foo" }, ayano), // ayano posts
|
||||
(msg) => msg.type === "note" && msg.body.text === "foo",
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("フォローしていないローカルユーザーの投稿が流れる", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"hybridTimeline", // ayano:Hybrid
|
||||
() => api("notes/create", { text: "foo" }, chitose), // chitose posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === chitose.id, // wait chitose
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("フォローしているリモートユーザーの投稿が流れる", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"hybridTimeline", // ayano:Hybrid
|
||||
() => api("notes/create", { text: "foo" }, akari), // akari posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === akari.id, // wait akari
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("フォローしていないリモートユーザーの投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"hybridTimeline", // ayano:Hybrid
|
||||
() => api("notes/create", { text: "foo" }, chinatsu), // chinatsu posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === chinatsu.id, // wait chinatsu
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
it("フォローしているユーザーのダイレクト投稿が流れる", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"hybridTimeline", // ayano:Hybrid
|
||||
() =>
|
||||
api(
|
||||
"notes/create",
|
||||
{
|
||||
text: "foo",
|
||||
visibility: "specified",
|
||||
visibleUserIds: [ayano.id],
|
||||
},
|
||||
kyoko,
|
||||
),
|
||||
(msg) => msg.type === "note" && msg.body.userId === kyoko.id, // wait kyoko
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("フォローしているユーザーのホーム投稿が流れる", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"hybridTimeline", // ayano:Hybrid
|
||||
() => api("notes/create", { text: "foo", visibility: "home" }, kyoko),
|
||||
(msg) => msg.type === "note" && msg.body.userId === kyoko.id, // wait kyoko
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("フォローしていないローカルユーザーのホーム投稿は流れない", async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"hybridTimeline", // ayano:Hybrid
|
||||
() =>
|
||||
api("notes/create", { text: "foo", visibility: "home" }, chitose),
|
||||
(msg) => msg.type === "note" && msg.body.userId === chitose.id,
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
it("フォローしていないローカルユーザーのフォロワー宛て投稿は流れない", () =>
|
||||
async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"hybridTimeline", // ayano:Hybrid
|
||||
() =>
|
||||
api(
|
||||
"notes/create",
|
||||
{ text: "foo", visibility: "followers" },
|
||||
chitose,
|
||||
),
|
||||
(msg) => msg.type === "note" && msg.body.userId === chitose.id,
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Global Timeline", () => {
|
||||
it("フォローしていないローカルユーザーの投稿が流れる", () => async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"globalTimeline", // ayano:Global
|
||||
() => api("notes/create", { text: "foo" }, chitose), // chitose posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === chitose.id, // wait chitose
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("フォローしていないリモートユーザーの投稿が流れる", () => async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"globalTimeline", // ayano:Global
|
||||
() => api("notes/create", { text: "foo" }, chinatsu), // chinatsu posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === chinatsu.id, // wait chinatsu
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("ホーム投稿は流れない", () => async () => {
|
||||
const fired = await waitFire(
|
||||
ayano,
|
||||
"globalTimeline", // ayano:Global
|
||||
() => api("notes/create", { text: "foo", visibility: "home" }, kyoko), // kyoko posts
|
||||
(msg) => msg.type === "note" && msg.body.userId === kyoko.id, // wait kyoko
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("UserList Timeline", () => {
|
||||
it("リストに入れているユーザーの投稿が流れる", () => async () => {
|
||||
const fired = await waitFire(
|
||||
chitose,
|
||||
"userList",
|
||||
() => api("notes/create", { text: "foo" }, ayano),
|
||||
(msg) => msg.type === "note" && msg.body.userId === ayano.id,
|
||||
{ listId: list.id },
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
it("リストに入れていないユーザーの投稿は流れない", () => async () => {
|
||||
const fired = await waitFire(
|
||||
chitose,
|
||||
"userList",
|
||||
() => api("notes/create", { text: "foo" }, chinatsu),
|
||||
(msg) => msg.type === "note" && msg.body.userId === chinatsu.id,
|
||||
{ listId: list.id },
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
// #4471
|
||||
it("リストに入れているユーザーのダイレクト投稿が流れる", () =>
|
||||
async () => {
|
||||
const fired = await waitFire(
|
||||
chitose,
|
||||
"userList",
|
||||
() =>
|
||||
api(
|
||||
"notes/create",
|
||||
{
|
||||
text: "foo",
|
||||
visibility: "specified",
|
||||
visibleUserIds: [chitose.id],
|
||||
},
|
||||
ayano,
|
||||
),
|
||||
(msg) => msg.type === "note" && msg.body.userId === ayano.id,
|
||||
{ listId: list.id },
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, true);
|
||||
});
|
||||
|
||||
// #4335
|
||||
it("リストに入れているがフォローはしてないユーザーのフォロワー宛て投稿は流れない", () =>
|
||||
async () => {
|
||||
const fired = await waitFire(
|
||||
chitose,
|
||||
"userList",
|
||||
() =>
|
||||
api(
|
||||
"notes/create",
|
||||
{ text: "foo", visibility: "followers" },
|
||||
kyoko,
|
||||
),
|
||||
(msg) => msg.type === "note" && msg.body.userId === kyoko.id,
|
||||
{ listId: list.id },
|
||||
);
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Hashtag Timeline", () => {
|
||||
it("指定したハッシュタグの投稿が流れる", () =>
|
||||
new Promise<void>(async (done) => {
|
||||
const ws = await connectStream(
|
||||
chitose,
|
||||
"hashtag",
|
||||
({ type, body }) => {
|
||||
if (type == "note") {
|
||||
assert.deepStrictEqual(body.text, "#foo");
|
||||
ws.close();
|
||||
done();
|
||||
}
|
||||
},
|
||||
{
|
||||
q: [["foo"]],
|
||||
},
|
||||
);
|
||||
|
||||
post(chitose, {
|
||||
text: "#foo",
|
||||
});
|
||||
}));
|
||||
|
||||
it("指定したハッシュタグの投稿が流れる (AND)", () =>
|
||||
new Promise<void>(async (done) => {
|
||||
let fooCount = 0;
|
||||
let barCount = 0;
|
||||
let fooBarCount = 0;
|
||||
|
||||
const ws = await connectStream(
|
||||
chitose,
|
||||
"hashtag",
|
||||
({ type, body }) => {
|
||||
if (type == "note") {
|
||||
if (body.text === "#foo") fooCount++;
|
||||
if (body.text === "#bar") barCount++;
|
||||
if (body.text === "#foo #bar") fooBarCount++;
|
||||
}
|
||||
},
|
||||
{
|
||||
q: [["foo", "bar"]],
|
||||
},
|
||||
);
|
||||
|
||||
post(chitose, {
|
||||
text: "#foo",
|
||||
});
|
||||
|
||||
post(chitose, {
|
||||
text: "#bar",
|
||||
});
|
||||
|
||||
post(chitose, {
|
||||
text: "#foo #bar",
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
assert.strictEqual(fooCount, 0);
|
||||
assert.strictEqual(barCount, 0);
|
||||
assert.strictEqual(fooBarCount, 1);
|
||||
ws.close();
|
||||
done();
|
||||
}, 3000);
|
||||
}));
|
||||
|
||||
it("指定したハッシュタグの投稿が流れる (OR)", () =>
|
||||
new Promise<void>(async (done) => {
|
||||
let fooCount = 0;
|
||||
let barCount = 0;
|
||||
let fooBarCount = 0;
|
||||
let piyoCount = 0;
|
||||
|
||||
const ws = await connectStream(
|
||||
chitose,
|
||||
"hashtag",
|
||||
({ type, body }) => {
|
||||
if (type == "note") {
|
||||
if (body.text === "#foo") fooCount++;
|
||||
if (body.text === "#bar") barCount++;
|
||||
if (body.text === "#foo #bar") fooBarCount++;
|
||||
if (body.text === "#piyo") piyoCount++;
|
||||
}
|
||||
},
|
||||
{
|
||||
q: [["foo"], ["bar"]],
|
||||
},
|
||||
);
|
||||
|
||||
post(chitose, {
|
||||
text: "#foo",
|
||||
});
|
||||
|
||||
post(chitose, {
|
||||
text: "#bar",
|
||||
});
|
||||
|
||||
post(chitose, {
|
||||
text: "#foo #bar",
|
||||
});
|
||||
|
||||
post(chitose, {
|
||||
text: "#piyo",
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
assert.strictEqual(fooCount, 1);
|
||||
assert.strictEqual(barCount, 1);
|
||||
assert.strictEqual(fooBarCount, 1);
|
||||
assert.strictEqual(piyoCount, 0);
|
||||
ws.close();
|
||||
done();
|
||||
}, 3000);
|
||||
}));
|
||||
|
||||
it("指定したハッシュタグの投稿が流れる (AND + OR)", () =>
|
||||
new Promise<void>(async (done) => {
|
||||
let fooCount = 0;
|
||||
let barCount = 0;
|
||||
let fooBarCount = 0;
|
||||
let piyoCount = 0;
|
||||
let waaaCount = 0;
|
||||
|
||||
const ws = await connectStream(
|
||||
chitose,
|
||||
"hashtag",
|
||||
({ type, body }) => {
|
||||
if (type == "note") {
|
||||
if (body.text === "#foo") fooCount++;
|
||||
if (body.text === "#bar") barCount++;
|
||||
if (body.text === "#foo #bar") fooBarCount++;
|
||||
if (body.text === "#piyo") piyoCount++;
|
||||
if (body.text === "#waaa") waaaCount++;
|
||||
}
|
||||
},
|
||||
{
|
||||
q: [["foo", "bar"], ["piyo"]],
|
||||
},
|
||||
);
|
||||
|
||||
post(chitose, {
|
||||
text: "#foo",
|
||||
});
|
||||
|
||||
post(chitose, {
|
||||
text: "#bar",
|
||||
});
|
||||
|
||||
post(chitose, {
|
||||
text: "#foo #bar",
|
||||
});
|
||||
|
||||
post(chitose, {
|
||||
text: "#piyo",
|
||||
});
|
||||
|
||||
post(chitose, {
|
||||
text: "#waaa",
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
assert.strictEqual(fooCount, 0);
|
||||
assert.strictEqual(barCount, 0);
|
||||
assert.strictEqual(fooBarCount, 1);
|
||||
assert.strictEqual(piyoCount, 1);
|
||||
assert.strictEqual(waaaCount, 0);
|
||||
ws.close();
|
||||
done();
|
||||
}, 3000);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,161 +0,0 @@
|
|||
process.env.NODE_ENV = "test";
|
||||
|
||||
import * as assert from "node:assert";
|
||||
import type * as childProcess from "node:child_process";
|
||||
import {
|
||||
async,
|
||||
connectStream,
|
||||
post,
|
||||
react,
|
||||
request,
|
||||
shutdownServer,
|
||||
signup,
|
||||
startServer,
|
||||
} from "./utils.js";
|
||||
|
||||
describe("Note thread mute", () => {
|
||||
let p: childProcess.ChildProcess;
|
||||
|
||||
let alice: any;
|
||||
let bob: any;
|
||||
let carol: any;
|
||||
|
||||
before(async () => {
|
||||
p = await startServer();
|
||||
alice = await signup({ username: "alice" });
|
||||
bob = await signup({ username: "bob" });
|
||||
carol = await signup({ username: "carol" });
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await shutdownServer(p);
|
||||
});
|
||||
|
||||
it("notes/mentions にミュートしているスレッドの投稿が含まれない", async(async () => {
|
||||
const bobNote = await post(bob, { text: "@alice @carol root note" });
|
||||
const aliceReply = await post(alice, {
|
||||
replyId: bobNote.id,
|
||||
text: "@bob @carol child note",
|
||||
});
|
||||
|
||||
await request("/notes/thread-muting/create", { noteId: bobNote.id }, alice);
|
||||
|
||||
const carolReply = await post(carol, {
|
||||
replyId: bobNote.id,
|
||||
text: "@bob @alice child note",
|
||||
});
|
||||
const carolReplyWithoutMention = await post(carol, {
|
||||
replyId: aliceReply.id,
|
||||
text: "child note",
|
||||
});
|
||||
|
||||
const res = await request("/notes/mentions", {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === bobNote.id),
|
||||
false,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === carolReply.id),
|
||||
false,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === carolReplyWithoutMention.id),
|
||||
false,
|
||||
);
|
||||
}));
|
||||
|
||||
it("ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない", async(async () => {
|
||||
// 状態リセット
|
||||
await request("/i/read-all-unread-notes", {}, alice);
|
||||
|
||||
const bobNote = await post(bob, { text: "@alice @carol root note" });
|
||||
|
||||
await request("/notes/thread-muting/create", { noteId: bobNote.id }, alice);
|
||||
|
||||
const carolReply = await post(carol, {
|
||||
replyId: bobNote.id,
|
||||
text: "@bob @alice child note",
|
||||
});
|
||||
|
||||
const res = await request("/i", {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.body.hasUnreadMentions, false);
|
||||
}));
|
||||
|
||||
it("ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない", () =>
|
||||
new Promise(async (done) => {
|
||||
// 状態リセット
|
||||
await request("/i/read-all-unread-notes", {}, alice);
|
||||
|
||||
const bobNote = await post(bob, { text: "@alice @carol root note" });
|
||||
|
||||
await request(
|
||||
"/notes/thread-muting/create",
|
||||
{ noteId: bobNote.id },
|
||||
alice,
|
||||
);
|
||||
|
||||
let fired = false;
|
||||
|
||||
const ws = await connectStream(alice, "main", async ({ type, body }) => {
|
||||
if (type === "unreadMention") {
|
||||
if (body === bobNote.id) return;
|
||||
fired = true;
|
||||
}
|
||||
});
|
||||
|
||||
const carolReply = await post(carol, {
|
||||
replyId: bobNote.id,
|
||||
text: "@bob @alice child note",
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
assert.strictEqual(fired, false);
|
||||
ws.close();
|
||||
done();
|
||||
}, 5000);
|
||||
}));
|
||||
|
||||
it("i/notifications にミュートしているスレッドの通知が含まれない", async(async () => {
|
||||
const bobNote = await post(bob, { text: "@alice @carol root note" });
|
||||
const aliceReply = await post(alice, {
|
||||
replyId: bobNote.id,
|
||||
text: "@bob @carol child note",
|
||||
});
|
||||
|
||||
await request("/notes/thread-muting/create", { noteId: bobNote.id }, alice);
|
||||
|
||||
const carolReply = await post(carol, {
|
||||
replyId: bobNote.id,
|
||||
text: "@bob @alice child note",
|
||||
});
|
||||
const carolReplyWithoutMention = await post(carol, {
|
||||
replyId: aliceReply.id,
|
||||
text: "child note",
|
||||
});
|
||||
|
||||
const res = await request("/i/notifications", {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(
|
||||
res.body.some(
|
||||
(notification: any) => notification.note.id === carolReply.id,
|
||||
),
|
||||
false,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some(
|
||||
(notification: any) =>
|
||||
notification.note.id === carolReplyWithoutMention.id,
|
||||
),
|
||||
false,
|
||||
);
|
||||
|
||||
// NOTE: bobの投稿はスレッドミュート前に行われたため通知に含まれていてもよい
|
||||
}));
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"noEmitOnError": false,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedParameters": false,
|
||||
"noUnusedLocals": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"declaration": false,
|
||||
"sourceMap": true,
|
||||
"target": "es2021",
|
||||
"module": "es2020",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"removeComments": false,
|
||||
"noLib": false,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["../src/*"]
|
||||
},
|
||||
"typeRoots": ["../node_modules/@types", "../src/@types"],
|
||||
"lib": ["esnext"]
|
||||
},
|
||||
"compileOnSave": false,
|
||||
"include": ["./**/*.ts"]
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
process.env.NODE_ENV = "test";
|
||||
|
||||
import * as assert from "node:assert";
|
||||
import type * as childProcess from "node:child_process";
|
||||
import {
|
||||
async,
|
||||
post,
|
||||
request,
|
||||
shutdownServer,
|
||||
signup,
|
||||
startServer,
|
||||
uploadUrl,
|
||||
} from "./utils.js";
|
||||
|
||||
describe("users/notes", () => {
|
||||
let p: childProcess.ChildProcess;
|
||||
|
||||
let alice: any;
|
||||
let jpgNote: any;
|
||||
let pngNote: any;
|
||||
let jpgPngNote: any;
|
||||
|
||||
before(async () => {
|
||||
p = await startServer();
|
||||
alice = await signup({ username: "alice" });
|
||||
const jpg = await uploadUrl(
|
||||
alice,
|
||||
"https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg",
|
||||
);
|
||||
const png = await uploadUrl(
|
||||
alice,
|
||||
"https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.png",
|
||||
);
|
||||
jpgNote = await post(alice, {
|
||||
fileIds: [jpg.id],
|
||||
});
|
||||
pngNote = await post(alice, {
|
||||
fileIds: [png.id],
|
||||
});
|
||||
jpgPngNote = await post(alice, {
|
||||
fileIds: [jpg.id, png.id],
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await shutdownServer(p);
|
||||
});
|
||||
|
||||
it("ファイルタイプ指定 (jpg)", async(async () => {
|
||||
const res = await request(
|
||||
"/users/notes",
|
||||
{
|
||||
userId: alice.id,
|
||||
fileType: ["image/jpeg"],
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.length, 2);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === jpgNote.id),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === jpgPngNote.id),
|
||||
true,
|
||||
);
|
||||
}));
|
||||
|
||||
it("ファイルタイプ指定 (jpg or png)", async(async () => {
|
||||
const res = await request(
|
||||
"/users/notes",
|
||||
{
|
||||
userId: alice.id,
|
||||
fileType: ["image/jpeg", "image/png"],
|
||||
},
|
||||
alice,
|
||||
);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(Array.isArray(res.body), true);
|
||||
assert.strictEqual(res.body.length, 3);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === jpgNote.id),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === pngNote.id),
|
||||
true,
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.body.some((note: any) => note.id === jpgPngNote.id),
|
||||
true,
|
||||
);
|
||||
}));
|
||||
});
|
|
@ -1,403 +0,0 @@
|
|||
import { SIGKILL } from "constants";
|
||||
import * as childProcess from "node:child_process";
|
||||
import * as fs from "node:fs";
|
||||
import * as http from "node:http";
|
||||
import * as path from "node:path";
|
||||
import { dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import type { Entities, endpoints } from "firefish-js";
|
||||
import FormData from "form-data";
|
||||
import got from "got";
|
||||
import fetch from "node-fetch";
|
||||
import { DataSource } from "typeorm";
|
||||
import WebSocket from "ws";
|
||||
import loadConfig from "../src/config/load.js";
|
||||
import { entities } from "../src/db/postgre.js";
|
||||
|
||||
const _filename = fileURLToPath(import.meta.url);
|
||||
const _dirname = dirname(_filename);
|
||||
|
||||
const config = loadConfig();
|
||||
export const port = config.port;
|
||||
|
||||
export const async = (fn: Function) => (done: Function) => {
|
||||
fn().then(
|
||||
() => {
|
||||
done();
|
||||
},
|
||||
(err: Error) => {
|
||||
done(err);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const api = async (endpoint: string, params: any, me?: any) => {
|
||||
endpoint = endpoint.replace(/^\//, "");
|
||||
|
||||
const auth = me
|
||||
? {
|
||||
i: me.token,
|
||||
}
|
||||
: {};
|
||||
|
||||
const res = await got<string>(`http://localhost:${port}/api/${endpoint}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(Object.assign(auth, params)),
|
||||
retry: {
|
||||
limit: 0,
|
||||
},
|
||||
hooks: {
|
||||
beforeError: [
|
||||
(error) => {
|
||||
const { response } = error;
|
||||
if (response && response.body) console.warn(response.body);
|
||||
return error;
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const status = res.statusCode;
|
||||
const body = res.statusCode !== 204 ? await JSON.parse(res.body) : null;
|
||||
|
||||
return {
|
||||
status,
|
||||
body,
|
||||
};
|
||||
};
|
||||
|
||||
export const request = async (
|
||||
endpoint: string,
|
||||
params: any,
|
||||
me?: any,
|
||||
): Promise<{ body: any; status: number }> => {
|
||||
const auth = me
|
||||
? {
|
||||
i: me.token,
|
||||
}
|
||||
: {};
|
||||
|
||||
const res = await fetch(`http://localhost:${port}/api${endpoint}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(Object.assign(auth, params)),
|
||||
});
|
||||
|
||||
const status = res.status;
|
||||
const body = res.status !== 204 ? await res.json().catch() : null;
|
||||
|
||||
return {
|
||||
body,
|
||||
status,
|
||||
};
|
||||
};
|
||||
|
||||
export const signup = async (params?: any): Promise<any> => {
|
||||
const q = Object.assign(
|
||||
{
|
||||
username: "test",
|
||||
password: "test",
|
||||
},
|
||||
params,
|
||||
);
|
||||
|
||||
const res = await api("signup", q);
|
||||
|
||||
return res.body;
|
||||
};
|
||||
|
||||
export const post = async (
|
||||
user: any,
|
||||
params?: Endpoints["notes/create"]["req"],
|
||||
): Promise<entities.Note> => {
|
||||
const q = Object.assign(
|
||||
{
|
||||
text: "test",
|
||||
},
|
||||
params,
|
||||
);
|
||||
|
||||
const res = await api("notes/create", q, user);
|
||||
|
||||
return res.body ? res.body.createdNote : null;
|
||||
};
|
||||
|
||||
export const react = async (
|
||||
user: any,
|
||||
note: any,
|
||||
reaction: string,
|
||||
): Promise<any> => {
|
||||
await api(
|
||||
"notes/reactions/create",
|
||||
{
|
||||
noteId: note.id,
|
||||
reaction: reaction,
|
||||
},
|
||||
user,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Upload file
|
||||
* @param user User
|
||||
* @param _path Optional, absolute path or relative from ./resources/
|
||||
*/
|
||||
export const uploadFile = async (user: any, _path?: string): Promise<any> => {
|
||||
const absPath =
|
||||
_path == null
|
||||
? `${_dirname}/resources/Lenna.jpg`
|
||||
: path.isAbsolute(_path)
|
||||
? _path
|
||||
: `${_dirname}/resources/${_path}`;
|
||||
|
||||
const formData = new FormData() as any;
|
||||
formData.append("i", user.token);
|
||||
formData.append("file", fs.createReadStream(absPath));
|
||||
formData.append("force", "true");
|
||||
|
||||
const res = await got<string>(
|
||||
`http://localhost:${port}/api/drive/files/create`,
|
||||
{
|
||||
method: "POST",
|
||||
body: formData,
|
||||
retry: {
|
||||
limit: 0,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const body = res.statusCode !== 204 ? await JSON.parse(res.body) : null;
|
||||
|
||||
return body;
|
||||
};
|
||||
|
||||
export const uploadUrl = async (user: any, url: string) => {
|
||||
let file: any;
|
||||
|
||||
const ws = await connectStream(user, "main", (msg) => {
|
||||
if (msg.type === "driveFileCreated") {
|
||||
file = msg.body;
|
||||
}
|
||||
});
|
||||
|
||||
await api(
|
||||
"drive/files/upload-from-url",
|
||||
{
|
||||
url,
|
||||
force: true,
|
||||
},
|
||||
user,
|
||||
);
|
||||
|
||||
await sleep(5000);
|
||||
ws.close();
|
||||
|
||||
return file;
|
||||
};
|
||||
|
||||
export function connectStream(
|
||||
user: any,
|
||||
channel: string,
|
||||
listener: (message: Record<string, any>) => any,
|
||||
params?: any,
|
||||
): Promise<WebSocket> {
|
||||
return new Promise((res, rej) => {
|
||||
const ws = new WebSocket(
|
||||
`ws://localhost:${port}/streaming?i=${user.token}`,
|
||||
);
|
||||
|
||||
ws.on("open", () => {
|
||||
ws.on("message", (data) => {
|
||||
const msg = JSON.parse(data.toString());
|
||||
if (msg.type === "channel" && msg.body.id === "a") {
|
||||
listener(msg.body);
|
||||
} else if (msg.type === "connected" && msg.body.id === "a") {
|
||||
res(ws);
|
||||
}
|
||||
});
|
||||
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "connect",
|
||||
body: {
|
||||
channel: channel,
|
||||
id: "a",
|
||||
pong: true,
|
||||
params: params,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export const waitFire = async (
|
||||
user: any,
|
||||
channel: string,
|
||||
trgr: () => any,
|
||||
cond: (msg: Record<string, any>) => boolean,
|
||||
params?: any,
|
||||
) => {
|
||||
return new Promise<boolean>(async (res, rej) => {
|
||||
let timer: NodeJS.Timeout;
|
||||
|
||||
let ws: WebSocket;
|
||||
try {
|
||||
ws = await connectStream(
|
||||
user,
|
||||
channel,
|
||||
(msg) => {
|
||||
if (cond(msg)) {
|
||||
ws.close();
|
||||
if (timer) clearTimeout(timer);
|
||||
res(true);
|
||||
}
|
||||
},
|
||||
params,
|
||||
);
|
||||
} catch (e) {
|
||||
rej(e);
|
||||
}
|
||||
|
||||
if (!ws!) return;
|
||||
|
||||
timer = setTimeout(() => {
|
||||
ws.close();
|
||||
res(false);
|
||||
}, 3000);
|
||||
|
||||
try {
|
||||
await trgr();
|
||||
} catch (e) {
|
||||
ws.close();
|
||||
if (timer) clearTimeout(timer);
|
||||
rej(e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const simpleGet = async (
|
||||
path: string,
|
||||
accept = "*/*",
|
||||
): Promise<{ status?: number; type?: string; location?: string }> => {
|
||||
// node-fetchだと3xxを取れない
|
||||
return await new Promise((resolve, reject) => {
|
||||
const req = http.request(
|
||||
`http://localhost:${port}${path}`,
|
||||
{
|
||||
headers: {
|
||||
Accept: accept,
|
||||
},
|
||||
},
|
||||
(res) => {
|
||||
if (res.statusCode! >= 400) {
|
||||
reject(res);
|
||||
} else {
|
||||
resolve({
|
||||
status: res.statusCode,
|
||||
type: res.headers["content-type"],
|
||||
location: res.headers.location,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
req.end();
|
||||
});
|
||||
};
|
||||
|
||||
export function launchServer(
|
||||
callbackSpawnedProcess: (p: childProcess.ChildProcess) => void,
|
||||
moreProcess: () => Promise<void> = async () => {},
|
||||
) {
|
||||
return (done: (err?: Error) => any) => {
|
||||
const p = childProcess.spawn("node", [_dirname + "/../index.js"], {
|
||||
stdio: ["inherit", "inherit", "inherit", "ipc"],
|
||||
env: { NODE_ENV: "test", PATH: process.env.PATH },
|
||||
});
|
||||
callbackSpawnedProcess(p);
|
||||
p.on("message", (message) => {
|
||||
if (message === "ok")
|
||||
moreProcess()
|
||||
.then(() => done())
|
||||
.catch((e) => done(e));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export async function initTestDb(justBorrow = false, initEntities?: any[]) {
|
||||
if (process.env.NODE_ENV !== "test") throw "NODE_ENV is not a test";
|
||||
|
||||
const db = new DataSource({
|
||||
type: "postgres",
|
||||
host: config.db.host,
|
||||
port: config.db.port,
|
||||
username: config.db.user,
|
||||
password: config.db.pass,
|
||||
database: config.db.db,
|
||||
synchronize: true && !justBorrow,
|
||||
dropSchema: true && !justBorrow,
|
||||
entities: initEntities || entities,
|
||||
});
|
||||
|
||||
await db.initialize();
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
export function startServer(
|
||||
timeout = 60 * 1000,
|
||||
): Promise<childProcess.ChildProcess> {
|
||||
return new Promise((res, rej) => {
|
||||
const t = setTimeout(() => {
|
||||
p.kill(SIGKILL);
|
||||
rej("timeout to start");
|
||||
}, timeout);
|
||||
|
||||
const p = childProcess.spawn("node", [_dirname + "/../built/index.js"], {
|
||||
stdio: ["inherit", "inherit", "inherit", "ipc"],
|
||||
env: { NODE_ENV: "test", PATH: process.env.PATH },
|
||||
});
|
||||
|
||||
p.on("error", (e) => rej(e));
|
||||
|
||||
p.on("message", (message) => {
|
||||
if (message === "ok") {
|
||||
clearTimeout(t);
|
||||
res(p);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function shutdownServer(
|
||||
p: childProcess.ChildProcess,
|
||||
timeout = 20 * 1000,
|
||||
) {
|
||||
return new Promise((res, rej) => {
|
||||
const t = setTimeout(() => {
|
||||
p.kill(SIGKILL);
|
||||
res("force exit");
|
||||
}, timeout);
|
||||
|
||||
p.once("exit", () => {
|
||||
clearTimeout(t);
|
||||
res("exited");
|
||||
});
|
||||
|
||||
p.kill();
|
||||
});
|
||||
}
|
||||
|
||||
export function sleep(msec: number) {
|
||||
return new Promise<void>((res) => {
|
||||
setTimeout(() => {
|
||||
res();
|
||||
}, msec);
|
||||
});
|
||||
}
|
172
pnpm-lock.yaml
|
@ -388,9 +388,6 @@ importers:
|
|||
'@types/koa__router':
|
||||
specifier: 12.0.4
|
||||
version: 12.0.4
|
||||
'@types/mocha':
|
||||
specifier: 10.0.7
|
||||
version: 10.0.7
|
||||
'@types/node':
|
||||
specifier: 20.14.11
|
||||
version: 20.14.11
|
||||
|
@ -463,9 +460,6 @@ importers:
|
|||
cross-env:
|
||||
specifier: 7.0.3
|
||||
version: 7.0.3
|
||||
mocha:
|
||||
specifier: 10.7.0
|
||||
version: 10.7.0
|
||||
pug:
|
||||
specifier: 3.0.3
|
||||
version: 3.0.3
|
||||
|
@ -2490,9 +2484,6 @@ packages:
|
|||
'@types/minimist@1.2.5':
|
||||
resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
|
||||
|
||||
'@types/mocha@10.0.7':
|
||||
resolution: {integrity: sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==}
|
||||
|
||||
'@types/mute-stream@0.0.4':
|
||||
resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==}
|
||||
|
||||
|
@ -2779,10 +2770,6 @@ packages:
|
|||
ajv@8.17.1:
|
||||
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
||||
|
||||
ansi-colors@4.1.3:
|
||||
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
ansi-escapes@4.3.2:
|
||||
resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -2978,9 +2965,6 @@ packages:
|
|||
broadcast-channel@7.0.0:
|
||||
resolution: {integrity: sha512-a2tW0Ia1pajcPBOGUF2jXlDnvE9d5/dg6BG9h60OmRUcZVr/veUrU8vEQFwwQIhwG3KVzYwSk3v2nRRGFgQDXQ==}
|
||||
|
||||
browser-stdout@1.3.1:
|
||||
resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==}
|
||||
|
||||
browserify-zlib@0.1.4:
|
||||
resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==}
|
||||
|
||||
|
@ -3607,10 +3591,6 @@ packages:
|
|||
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
decamelize@4.0.0:
|
||||
resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
decimal.js@10.4.3:
|
||||
resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
|
||||
|
||||
|
@ -3706,10 +3686,6 @@ packages:
|
|||
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
|
||||
diff@5.2.0:
|
||||
resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
|
||||
dijkstrajs@1.0.3:
|
||||
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
|
||||
|
||||
|
@ -4027,17 +4003,9 @@ packages:
|
|||
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
find-up@5.0.0:
|
||||
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
fix-esm@1.0.1:
|
||||
resolution: {integrity: sha512-EZtb7wPXZS54GaGxaWxMlhd1DUDCnAg5srlYdu/1ZVeW+7wwR3Tp59nu52dXByFs3MBRq+SByx1wDOJpRvLEXw==}
|
||||
|
||||
flat@5.0.2:
|
||||
resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
|
||||
hasBin: true
|
||||
|
||||
fluent-ffmpeg@2.1.3:
|
||||
resolution: {integrity: sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -4170,11 +4138,6 @@ packages:
|
|||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
|
||||
glob@8.1.0:
|
||||
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
|
||||
engines: {node: '>=12'}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
|
||||
globals@11.12.0:
|
||||
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
|
||||
engines: {node: '>=4'}
|
||||
|
@ -4489,10 +4452,6 @@ packages:
|
|||
resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-plain-obj@2.1.0:
|
||||
resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-plain-obj@4.1.0:
|
||||
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -4955,10 +4914,6 @@ packages:
|
|||
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
locate-path@6.0.0:
|
||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
lodash-es@4.17.21:
|
||||
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
|
||||
|
||||
|
@ -5159,11 +5114,6 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
mocha@10.7.0:
|
||||
resolution: {integrity: sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
hasBin: true
|
||||
|
||||
mock-socket@9.3.1:
|
||||
resolution: {integrity: sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
@ -5380,10 +5330,6 @@ packages:
|
|||
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
p-locate@5.0.0:
|
||||
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
p-queue@6.6.2:
|
||||
resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -6819,9 +6765,6 @@ packages:
|
|||
resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
workerpool@6.5.1:
|
||||
resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==}
|
||||
|
||||
wrap-ansi@6.2.0:
|
||||
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -6915,10 +6858,6 @@ packages:
|
|||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yargs-unparser@2.0.0:
|
||||
resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
yargs@15.4.1:
|
||||
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -6991,7 +6930,7 @@ snapshots:
|
|||
'@babel/traverse': 7.24.7
|
||||
'@babel/types': 7.24.7
|
||||
convert-source-map: 2.0.0
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
gensync: 1.0.0-beta.2
|
||||
json5: 2.2.3
|
||||
semver: 6.3.1
|
||||
|
@ -7189,7 +7128,7 @@ snapshots:
|
|||
'@babel/helper-split-export-declaration': 7.24.7
|
||||
'@babel/parser': 7.24.7
|
||||
'@babel/types': 7.24.7
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
globals: 11.12.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
@ -7889,7 +7828,7 @@ snapshots:
|
|||
|
||||
'@koa/router@12.0.1':
|
||||
dependencies:
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
http-errors: 2.0.0
|
||||
koa-compose: 4.1.0
|
||||
methods: 1.1.2
|
||||
|
@ -7909,7 +7848,7 @@ snapshots:
|
|||
'@ladjs/koa-views@9.0.0(@babel/core@7.24.7)(@types/koa@2.15.0)(ejs@3.1.10)(lodash@4.17.21)(pug@3.0.3)':
|
||||
dependencies:
|
||||
'@ladjs/consolidate': 1.0.4(@babel/core@7.24.7)(ejs@3.1.10)(lodash@4.17.21)(pug@3.0.3)
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
get-paths: 0.0.7
|
||||
koa-send: 5.0.1
|
||||
mz: 2.7.0
|
||||
|
@ -7995,7 +7934,7 @@ snapshots:
|
|||
'@octokit/rest': 21.0.0
|
||||
clipanion: 3.2.1(typanion@3.14.0)
|
||||
colorette: 2.0.20
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
inquirer: 10.0.1
|
||||
js-yaml: 4.1.0
|
||||
lodash-es: 4.17.21
|
||||
|
@ -8018,7 +7957,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@napi-rs/lzma': 1.3.1
|
||||
'@napi-rs/tar': 0.1.1
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
@ -8670,8 +8609,6 @@ snapshots:
|
|||
|
||||
'@types/minimist@1.2.5': {}
|
||||
|
||||
'@types/mocha@10.0.7': {}
|
||||
|
||||
'@types/mute-stream@0.0.4':
|
||||
dependencies:
|
||||
'@types/node': 20.14.11
|
||||
|
@ -9005,7 +8942,7 @@ snapshots:
|
|||
|
||||
agent-base@7.1.1:
|
||||
dependencies:
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
@ -9027,8 +8964,6 @@ snapshots:
|
|||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
|
||||
ansi-colors@4.1.3: {}
|
||||
|
||||
ansi-escapes@4.3.2:
|
||||
dependencies:
|
||||
type-fest: 0.21.3
|
||||
|
@ -9255,8 +9190,6 @@ snapshots:
|
|||
p-queue: 6.6.2
|
||||
unload: 2.4.1
|
||||
|
||||
browser-stdout@1.3.1: {}
|
||||
|
||||
browserify-zlib@0.1.4:
|
||||
dependencies:
|
||||
pako: 0.2.9
|
||||
|
@ -9755,11 +9688,9 @@ snapshots:
|
|||
dependencies:
|
||||
ms: 2.1.2
|
||||
|
||||
debug@4.3.5(supports-color@8.1.1):
|
||||
debug@4.3.5:
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
optionalDependencies:
|
||||
supports-color: 8.1.1
|
||||
|
||||
decamelize-keys@1.1.1:
|
||||
dependencies:
|
||||
|
@ -9768,8 +9699,6 @@ snapshots:
|
|||
|
||||
decamelize@1.2.0: {}
|
||||
|
||||
decamelize@4.0.0: {}
|
||||
|
||||
decimal.js@10.4.3: {}
|
||||
|
||||
decompress-response@6.0.0:
|
||||
|
@ -9862,8 +9791,6 @@ snapshots:
|
|||
|
||||
diff@4.0.2: {}
|
||||
|
||||
diff@5.2.0: {}
|
||||
|
||||
dijkstrajs@1.0.3: {}
|
||||
|
||||
dir-glob@3.0.1:
|
||||
|
@ -10216,11 +10143,6 @@ snapshots:
|
|||
locate-path: 5.0.0
|
||||
path-exists: 4.0.0
|
||||
|
||||
find-up@5.0.0:
|
||||
dependencies:
|
||||
locate-path: 6.0.0
|
||||
path-exists: 4.0.0
|
||||
|
||||
fix-esm@1.0.1:
|
||||
dependencies:
|
||||
'@babel/core': 7.24.7
|
||||
|
@ -10229,8 +10151,6 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
flat@5.0.2: {}
|
||||
|
||||
fluent-ffmpeg@2.1.3:
|
||||
dependencies:
|
||||
async: 0.2.10
|
||||
|
@ -10364,14 +10284,6 @@ snapshots:
|
|||
once: 1.4.0
|
||||
path-is-absolute: 1.0.1
|
||||
|
||||
glob@8.1.0:
|
||||
dependencies:
|
||||
fs.realpath: 1.0.0
|
||||
inflight: 1.0.6
|
||||
inherits: 2.0.4
|
||||
minimatch: 5.1.6
|
||||
once: 1.4.0
|
||||
|
||||
globals@11.12.0: {}
|
||||
|
||||
globby@11.1.0:
|
||||
|
@ -10524,7 +10436,7 @@ snapshots:
|
|||
http-proxy-agent@7.0.2:
|
||||
dependencies:
|
||||
agent-base: 7.1.1
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
@ -10541,7 +10453,7 @@ snapshots:
|
|||
https-proxy-agent@7.0.5:
|
||||
dependencies:
|
||||
agent-base: 7.1.1
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
@ -10607,7 +10519,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@ioredis/commands': 1.2.0
|
||||
cluster-key-slot: 1.1.2
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
denque: 2.1.0
|
||||
lodash.defaults: 4.2.0
|
||||
lodash.isarguments: 3.1.0
|
||||
|
@ -10692,8 +10604,6 @@ snapshots:
|
|||
|
||||
is-plain-obj@1.1.0: {}
|
||||
|
||||
is-plain-obj@2.1.0: {}
|
||||
|
||||
is-plain-obj@4.1.0: {}
|
||||
|
||||
is-plain-object@5.0.0: {}
|
||||
|
@ -10765,7 +10675,7 @@ snapshots:
|
|||
|
||||
istanbul-lib-source-maps@4.0.1:
|
||||
dependencies:
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
source-map: 0.6.1
|
||||
transitivePeerDependencies:
|
||||
|
@ -11293,7 +11203,7 @@ snapshots:
|
|||
|
||||
koa-mount@4.0.0:
|
||||
dependencies:
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
koa-compose: 4.1.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
@ -11302,7 +11212,7 @@ snapshots:
|
|||
|
||||
koa-router@10.1.1:
|
||||
dependencies:
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
http-errors: 1.8.1
|
||||
koa-compose: 4.1.0
|
||||
methods: 1.1.2
|
||||
|
@ -11312,7 +11222,7 @@ snapshots:
|
|||
|
||||
koa-send@5.0.1:
|
||||
dependencies:
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
http-errors: 1.8.1
|
||||
resolve-path: 1.4.0
|
||||
transitivePeerDependencies:
|
||||
|
@ -11328,7 +11238,7 @@ snapshots:
|
|||
koa-views@7.0.2(@types/koa@2.15.0)(ejs@3.1.10)(lodash@4.17.21)(pug@3.0.3):
|
||||
dependencies:
|
||||
consolidate: 0.16.0(ejs@3.1.10)(lodash@4.17.21)(pug@3.0.3)
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
get-paths: 0.0.7
|
||||
koa-send: 5.0.1
|
||||
mz: 2.7.0
|
||||
|
@ -11399,7 +11309,7 @@ snapshots:
|
|||
content-disposition: 0.5.4
|
||||
content-type: 1.0.5
|
||||
cookies: 0.8.0
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
delegates: 1.0.0
|
||||
depd: 2.0.0
|
||||
destroy: 1.2.0
|
||||
|
@ -11427,7 +11337,7 @@ snapshots:
|
|||
content-disposition: 0.5.4
|
||||
content-type: 1.0.5
|
||||
cookies: 0.9.1
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
delegates: 1.0.0
|
||||
depd: 2.0.0
|
||||
destroy: 1.2.0
|
||||
|
@ -11476,10 +11386,6 @@ snapshots:
|
|||
dependencies:
|
||||
p-locate: 4.1.0
|
||||
|
||||
locate-path@6.0.0:
|
||||
dependencies:
|
||||
p-locate: 5.0.0
|
||||
|
||||
lodash-es@4.17.21: {}
|
||||
|
||||
lodash.assignin@4.2.0: {}
|
||||
|
@ -11643,29 +11549,6 @@ snapshots:
|
|||
|
||||
mkdirp@2.1.6: {}
|
||||
|
||||
mocha@10.7.0:
|
||||
dependencies:
|
||||
ansi-colors: 4.1.3
|
||||
browser-stdout: 1.3.1
|
||||
chokidar: 3.6.0
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
diff: 5.2.0
|
||||
escape-string-regexp: 4.0.0
|
||||
find-up: 5.0.0
|
||||
glob: 8.1.0
|
||||
he: 1.2.0
|
||||
js-yaml: 4.1.0
|
||||
log-symbols: 4.1.0
|
||||
minimatch: 5.1.6
|
||||
ms: 2.1.3
|
||||
serialize-javascript: 6.0.2
|
||||
strip-json-comments: 3.1.1
|
||||
supports-color: 8.1.1
|
||||
workerpool: 6.5.1
|
||||
yargs: 16.2.0
|
||||
yargs-parser: 20.2.9
|
||||
yargs-unparser: 2.0.0
|
||||
|
||||
mock-socket@9.3.1: {}
|
||||
|
||||
moment@2.30.1: {}
|
||||
|
@ -11854,10 +11737,6 @@ snapshots:
|
|||
dependencies:
|
||||
p-limit: 2.3.0
|
||||
|
||||
p-locate@5.0.0:
|
||||
dependencies:
|
||||
p-limit: 3.1.0
|
||||
|
||||
p-queue@6.6.2:
|
||||
dependencies:
|
||||
eventemitter3: 4.0.7
|
||||
|
@ -12339,7 +12218,7 @@ snapshots:
|
|||
|
||||
redis-semaphore@5.6.0(ioredis@5.4.1):
|
||||
dependencies:
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
optionalDependencies:
|
||||
ioredis: 5.4.1
|
||||
transitivePeerDependencies:
|
||||
|
@ -13000,7 +12879,7 @@ snapshots:
|
|||
chalk: 4.1.2
|
||||
cli-highlight: 2.1.11
|
||||
dayjs: 1.11.11
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
dotenv: 16.4.5
|
||||
glob: 10.4.5
|
||||
mkdirp: 2.1.6
|
||||
|
@ -13126,7 +13005,7 @@ snapshots:
|
|||
vite-plugin-compression@0.5.1(vite@5.3.4(@types/node@20.14.11)(sass@1.77.8)(terser@5.31.2)):
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
debug: 4.3.5
|
||||
fs-extra: 10.1.0
|
||||
vite: 5.3.4(@types/node@20.14.11)(sass@1.77.8)(terser@5.31.2)
|
||||
transitivePeerDependencies:
|
||||
|
@ -13295,8 +13174,6 @@ snapshots:
|
|||
assert-never: 1.3.0
|
||||
babel-walk: 3.0.0-canary-5
|
||||
|
||||
workerpool@6.5.1: {}
|
||||
|
||||
wrap-ansi@6.2.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
|
@ -13372,13 +13249,6 @@ snapshots:
|
|||
|
||||
yargs-parser@21.1.1: {}
|
||||
|
||||
yargs-unparser@2.0.0:
|
||||
dependencies:
|
||||
camelcase: 6.3.0
|
||||
decamelize: 4.0.0
|
||||
flat: 5.0.2
|
||||
is-plain-obj: 2.1.0
|
||||
|
||||
yargs@15.4.1:
|
||||
dependencies:
|
||||
cliui: 6.0.0
|
||||
|
|