chore: More flakey test improvements (#5801)
This commit is contained in:
@@ -94,7 +94,7 @@ jobs:
|
||||
name: test
|
||||
command: |
|
||||
TESTFILES=$(circleci tests glob "server/**/*.test.ts" | circleci tests split)
|
||||
yarn test $TESTFILES
|
||||
yarn test --maxWorkers=2 $TESTFILES
|
||||
bundle-size:
|
||||
<<: *defaults
|
||||
environment:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"workerIdleMemoryLimit": "0.75",
|
||||
"maxWorkers": 2,
|
||||
"maxConcurrency": 1,
|
||||
"maxWorkers": "50%",
|
||||
"projects": [
|
||||
{
|
||||
"displayName": "server",
|
||||
|
||||
@@ -3,13 +3,11 @@ import SigninEmail from "@server/emails/templates/SigninEmail";
|
||||
import WelcomeEmail from "@server/emails/templates/WelcomeEmail";
|
||||
import { AuthenticationProvider } from "@server/models";
|
||||
import { buildUser, buildGuestUser, buildTeam } from "@server/test/factories";
|
||||
import { getTestServer, setCloudHosted } from "@server/test/support";
|
||||
import { getTestServer } from "@server/test/support";
|
||||
|
||||
const server = getTestServer();
|
||||
|
||||
describe("email", () => {
|
||||
beforeEach(setCloudHosted);
|
||||
|
||||
it("should require email param", async () => {
|
||||
const res = await server.post("/auth/email", {
|
||||
body: {},
|
||||
@@ -49,11 +47,11 @@ describe("email", () => {
|
||||
// Disable all the auth providers
|
||||
await AuthenticationProvider.update(
|
||||
{
|
||||
teamId: team.id,
|
||||
enabled: false,
|
||||
},
|
||||
{
|
||||
where: {
|
||||
teamId: team.id,
|
||||
enabled: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import randomstring from "randomstring";
|
||||
import { IntegrationService } from "@shared/types";
|
||||
import env from "@server/env";
|
||||
import { IntegrationAuthentication, SearchQuery } from "@server/models";
|
||||
@@ -17,7 +18,7 @@ jest.mock("../slack", () => ({
|
||||
const server = getTestServer();
|
||||
|
||||
describe("#hooks.unfurl", () => {
|
||||
it("should return documents", async () => {
|
||||
it("should return documents with matching SSO user", async () => {
|
||||
const user = await buildUser();
|
||||
const document = await buildDocument({
|
||||
userId: user.id,
|
||||
@@ -28,18 +29,19 @@ describe("#hooks.unfurl", () => {
|
||||
service: IntegrationService.Slack,
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
token: "",
|
||||
token: randomstring.generate(32),
|
||||
});
|
||||
|
||||
const res = await server.post("/api/hooks.unfurl", {
|
||||
body: {
|
||||
token: env.SLACK_VERIFICATION_TOKEN,
|
||||
team_id: "TXXXXXXXX",
|
||||
api_app_id: "AXXXXXXXXX",
|
||||
team_id: `T${randomstring.generate(8)}`,
|
||||
api_app_id: `A${randomstring.generate(8)}`,
|
||||
event: {
|
||||
type: "link_shared",
|
||||
channel: "Cxxxxxx",
|
||||
channel: `C${randomstring.generate(8)}`,
|
||||
user: user.authentications[0].providerId,
|
||||
message_ts: "123456789.9875",
|
||||
message_ts: randomstring.generate(12),
|
||||
links: [
|
||||
{
|
||||
domain: "getoutline.com",
|
||||
|
||||
@@ -70,20 +70,31 @@ router.post(
|
||||
model: UserAuthentication,
|
||||
as: "authentications",
|
||||
required: true,
|
||||
separate: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
if (!user) {
|
||||
Logger.debug("plugins", "No user found for Slack user ID", {
|
||||
providerId: event.user,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const auth = await IntegrationAuthentication.findOne({
|
||||
where: {
|
||||
service: IntegrationService.Slack,
|
||||
teamId: user.teamId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!auth) {
|
||||
Logger.debug(
|
||||
"plugins",
|
||||
"No Slack integration authentication found for team",
|
||||
{
|
||||
teamId: user.teamId,
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
// get content for unfurled links
|
||||
|
||||
@@ -5,18 +5,16 @@ import { TeamDomain } from "@server/models";
|
||||
import Collection from "@server/models/Collection";
|
||||
import UserAuthentication from "@server/models/UserAuthentication";
|
||||
import { buildUser, buildTeam, buildAdmin } from "@server/test/factories";
|
||||
import { setCloudHosted, setSelfHosted } from "@server/test/support";
|
||||
import { setSelfHosted } from "@server/test/support";
|
||||
import accountProvisioner from "./accountProvisioner";
|
||||
|
||||
describe("accountProvisioner", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
describe("hosted", () => {
|
||||
beforeEach(setCloudHosted);
|
||||
|
||||
it("should create a new user and team", async () => {
|
||||
const spy = jest.spyOn(WelcomeEmail.prototype, "schedule");
|
||||
const email = faker.internet.email();
|
||||
const email = faker.internet.email().toLowerCase();
|
||||
const { user, team, isNewTeam, isNewUser } = await accountProvisioner({
|
||||
ip,
|
||||
user: {
|
||||
@@ -69,7 +67,7 @@ describe("accountProvisioner", () => {
|
||||
});
|
||||
const authentications = await existing.$get("authentications");
|
||||
const authentication = authentications[0];
|
||||
const newEmail = faker.internet.email();
|
||||
const newEmail = faker.internet.email().toLowerCase();
|
||||
const { user, isNewUser, isNewTeam } = await accountProvisioner({
|
||||
ip,
|
||||
user: {
|
||||
@@ -104,14 +102,15 @@ describe("accountProvisioner", () => {
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it.skip("should allow authentication by email matching", async () => {
|
||||
it("should allow authentication by email matching", async () => {
|
||||
const subdomain = faker.internet.domainWord();
|
||||
const existingTeam = await buildTeam({
|
||||
subdomain,
|
||||
});
|
||||
|
||||
const providers = await existingTeam.$get("authenticationProviders");
|
||||
const authenticationProvider = providers[0];
|
||||
const email = faker.internet.email();
|
||||
const email = faker.internet.email().toLowerCase();
|
||||
const userWithoutAuth = await buildUser({
|
||||
email,
|
||||
teamId: existingTeam.id,
|
||||
@@ -196,7 +195,7 @@ describe("accountProvisioner", () => {
|
||||
const admin = await buildAdmin({ teamId: existingTeam.id });
|
||||
const providers = await existingTeam.$get("authenticationProviders");
|
||||
const authenticationProvider = providers[0];
|
||||
const email = faker.internet.email();
|
||||
const email = faker.internet.email().toLowerCase();
|
||||
|
||||
await TeamDomain.create({
|
||||
teamId: existingTeam.id,
|
||||
@@ -299,7 +298,7 @@ describe("accountProvisioner", () => {
|
||||
"authenticationProviders"
|
||||
);
|
||||
const authenticationProvider = authenticationProviders[0];
|
||||
const email = faker.internet.email();
|
||||
const email = faker.internet.email().toLowerCase();
|
||||
const { user, isNewUser } = await accountProvisioner({
|
||||
ip,
|
||||
user: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Event } from "@server/models";
|
||||
import { buildDocument, buildUser } from "@server/test/factories";
|
||||
import { findLatestEvent } from "@server/test/support";
|
||||
import commentCreator from "./commentCreator";
|
||||
|
||||
describe("commentCreator", () => {
|
||||
@@ -32,7 +32,9 @@ describe("commentCreator", () => {
|
||||
ip,
|
||||
});
|
||||
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
expect(comment.documentId).toEqual(document.id);
|
||||
expect(comment.createdById).toEqual(user.id);
|
||||
expect(event!.name).toEqual("comments.create");
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Comment } from "@server/models";
|
||||
import { Comment, Event } from "@server/models";
|
||||
import { buildDocument, buildUser } from "@server/test/factories";
|
||||
import { findLatestEvent } from "@server/test/support";
|
||||
import commentDestroyer from "./commentDestroyer";
|
||||
|
||||
describe("commentDestroyer", () => {
|
||||
@@ -46,7 +45,9 @@ describe("commentDestroyer", () => {
|
||||
});
|
||||
expect(count).toEqual(0);
|
||||
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
expect(event!.name).toEqual("comments.delete");
|
||||
expect(event!.modelId).toEqual(comment.id);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Event } from "@server/models";
|
||||
import { sequelize } from "@server/storage/database";
|
||||
import { buildDocument, buildUser } from "@server/test/factories";
|
||||
import { findLatestEvent } from "@server/test/support";
|
||||
import documentUpdater from "./documentUpdater";
|
||||
|
||||
describe("documentUpdater", () => {
|
||||
@@ -22,7 +22,9 @@ describe("documentUpdater", () => {
|
||||
})
|
||||
);
|
||||
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
expect(document.lastModifiedById).toEqual(user.id);
|
||||
expect(event!.name).toEqual("documents.update");
|
||||
expect(event!.documentId).toEqual(document.id);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { NotificationEventType } from "@shared/types";
|
||||
import { Event } from "@server/models";
|
||||
import { sequelize } from "@server/storage/database";
|
||||
import {
|
||||
buildUser,
|
||||
@@ -6,7 +7,6 @@ import {
|
||||
buildDocument,
|
||||
buildCollection,
|
||||
} from "@server/test/factories";
|
||||
import { findLatestEvent } from "@server/test/support";
|
||||
import notificationUpdater from "./notificationUpdater";
|
||||
|
||||
describe("notificationUpdater", () => {
|
||||
@@ -46,7 +46,9 @@ describe("notificationUpdater", () => {
|
||||
transaction,
|
||||
})
|
||||
);
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
|
||||
expect(notification.viewedAt).not.toBe(null);
|
||||
expect(notification.archivedAt).toBe(null);
|
||||
@@ -89,7 +91,9 @@ describe("notificationUpdater", () => {
|
||||
transaction,
|
||||
})
|
||||
);
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
|
||||
expect(notification.viewedAt).toBe(null);
|
||||
expect(notification.archivedAt).toBe(null);
|
||||
@@ -131,7 +135,9 @@ describe("notificationUpdater", () => {
|
||||
transaction,
|
||||
})
|
||||
);
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
|
||||
expect(notification.viewedAt).toBe(null);
|
||||
expect(notification.archivedAt).not.toBe(null);
|
||||
@@ -174,7 +180,9 @@ describe("notificationUpdater", () => {
|
||||
transaction,
|
||||
})
|
||||
);
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
|
||||
expect(notification.viewedAt).toBe(null);
|
||||
expect(notification.archivedAt).toBeNull();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Event } from "@server/models";
|
||||
import { buildDocument, buildUser } from "@server/test/factories";
|
||||
import { findLatestEvent } from "@server/test/support";
|
||||
import pinCreator from "./pinCreator";
|
||||
|
||||
describe("pinCreator", () => {
|
||||
@@ -18,7 +18,9 @@ describe("pinCreator", () => {
|
||||
ip,
|
||||
});
|
||||
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
expect(pin.documentId).toEqual(document.id);
|
||||
expect(pin.collectionId).toEqual(null);
|
||||
expect(pin.createdById).toEqual(user.id);
|
||||
@@ -41,7 +43,9 @@ describe("pinCreator", () => {
|
||||
ip,
|
||||
});
|
||||
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
expect(pin.documentId).toEqual(document.id);
|
||||
expect(pin.collectionId).toEqual(document.collectionId);
|
||||
expect(pin.createdById).toEqual(user.id);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Pin } from "@server/models";
|
||||
import { Event, Pin } from "@server/models";
|
||||
import { buildDocument, buildUser } from "@server/test/factories";
|
||||
import { findLatestEvent } from "@server/test/support";
|
||||
import pinDestroyer from "./pinDestroyer";
|
||||
|
||||
describe("pinCreator", () => {
|
||||
@@ -34,7 +33,9 @@ describe("pinCreator", () => {
|
||||
});
|
||||
expect(count).toEqual(0);
|
||||
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
expect(event!.name).toEqual("pins.delete");
|
||||
expect(event!.modelId).toEqual(pin.id);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Event } from "@server/models";
|
||||
import { buildDocument, buildUser } from "@server/test/factories";
|
||||
import { findLatestEvent } from "@server/test/support";
|
||||
import revisionCreator from "./revisionCreator";
|
||||
|
||||
describe("revisionCreator", () => {
|
||||
@@ -16,7 +16,7 @@ describe("revisionCreator", () => {
|
||||
user,
|
||||
ip,
|
||||
});
|
||||
const event = await findLatestEvent({
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
expect(revision.documentId).toEqual(document.id);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Star, Event } from "@server/models";
|
||||
import { sequelize } from "@server/storage/database";
|
||||
import { buildDocument, buildUser } from "@server/test/factories";
|
||||
import { findLatestEvent } from "@server/test/support";
|
||||
import starCreator from "./starCreator";
|
||||
|
||||
describe("starCreator", () => {
|
||||
@@ -23,7 +22,9 @@ describe("starCreator", () => {
|
||||
})
|
||||
);
|
||||
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
expect(star.documentId).toEqual(document.id);
|
||||
expect(star.userId).toEqual(user.id);
|
||||
expect(star.index).toEqual("P");
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Star } from "@server/models";
|
||||
import { Event, Star } from "@server/models";
|
||||
import { buildDocument, buildUser } from "@server/test/factories";
|
||||
import { findLatestEvent } from "@server/test/support";
|
||||
import starDestroyer from "./starDestroyer";
|
||||
|
||||
describe("starDestroyer", () => {
|
||||
@@ -34,7 +33,9 @@ describe("starDestroyer", () => {
|
||||
});
|
||||
expect(count).toEqual(0);
|
||||
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
expect(event!.name).toEqual("stars.delete");
|
||||
expect(event!.modelId).toEqual(star.id);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Star } from "@server/models";
|
||||
import { Event, Star } from "@server/models";
|
||||
import { buildDocument, buildUser } from "@server/test/factories";
|
||||
import { findLatestEvent } from "@server/test/support";
|
||||
import starUpdater from "./starUpdater";
|
||||
|
||||
describe("starUpdater", () => {
|
||||
@@ -28,7 +27,9 @@ describe("starUpdater", () => {
|
||||
ip,
|
||||
});
|
||||
|
||||
const event = await findLatestEvent();
|
||||
const event = await Event.findLatest({
|
||||
teamId: user.teamId,
|
||||
});
|
||||
expect(star.documentId).toEqual(document.id);
|
||||
expect(star.userId).toEqual(user.id);
|
||||
expect(star.index).toEqual("h");
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { faker } from "@faker-js/faker";
|
||||
import TeamDomain from "@server/models/TeamDomain";
|
||||
import { buildTeam, buildUser } from "@server/test/factories";
|
||||
import { setCloudHosted, setSelfHosted } from "@server/test/support";
|
||||
import { setSelfHosted } from "@server/test/support";
|
||||
import teamProvisioner from "./teamProvisioner";
|
||||
|
||||
describe("teamProvisioner", () => {
|
||||
const ip = "127.0.0.1";
|
||||
|
||||
describe("hosted", () => {
|
||||
beforeEach(setCloudHosted);
|
||||
|
||||
it("should create team and authentication provider", async () => {
|
||||
const subdomain = faker.internet.domainWord();
|
||||
const result = await teamProvisioner({
|
||||
|
||||
@@ -26,7 +26,8 @@ type LogCategory =
|
||||
| "queue"
|
||||
| "websockets"
|
||||
| "database"
|
||||
| "utils";
|
||||
| "utils"
|
||||
| "plugins";
|
||||
type Extra = Record<string, any>;
|
||||
|
||||
class Logger {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SaveOptions } from "sequelize";
|
||||
import type { SaveOptions, WhereOptions } from "sequelize";
|
||||
import {
|
||||
ForeignKey,
|
||||
AfterSave,
|
||||
@@ -111,6 +111,19 @@ class Event extends IdModel {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the latest event matching the where clause
|
||||
*
|
||||
* @param where The options to match against
|
||||
* @returns A promise resolving to the latest event or null
|
||||
*/
|
||||
static findLatest(where: WhereOptions) {
|
||||
return this.findOne({
|
||||
where,
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
}
|
||||
|
||||
static ACTIVITY_EVENTS: TEvent["name"][] = [
|
||||
"collections.create",
|
||||
"collections.delete",
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { buildAdmin, buildTeam } from "@server/test/factories";
|
||||
import { setCloudHosted } from "@server/test/support";
|
||||
import TeamDomain from "./TeamDomain";
|
||||
|
||||
describe("team domain model", () => {
|
||||
beforeEach(setCloudHosted);
|
||||
|
||||
describe("create", () => {
|
||||
it("should allow creation of domains", async () => {
|
||||
const team = await buildTeam();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { faker } from "@faker-js/faker";
|
||||
import { CollectionPermission } from "@shared/types";
|
||||
import { buildUser, buildTeam, buildCollection } from "@server/test/factories";
|
||||
import CollectionUser from "./CollectionUser";
|
||||
@@ -42,10 +43,11 @@ describe("user model", () => {
|
||||
|
||||
describe("availableTeams", () => {
|
||||
it("should return teams where another user with the same email exists", async () => {
|
||||
const email = faker.internet.email().toLowerCase();
|
||||
const user = await buildUser({
|
||||
email: "user-available-teams@example.com",
|
||||
email,
|
||||
});
|
||||
const anotherUser = await buildUser({ email: user.email });
|
||||
const anotherUser = await buildUser({ email });
|
||||
|
||||
const response = await user.availableTeams();
|
||||
expect(response.length).toEqual(2);
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
import { buildUser, buildTeam, buildAdmin } from "@server/test/factories";
|
||||
import { setCloudHosted, setSelfHosted } from "@server/test/support";
|
||||
import { setSelfHosted } from "@server/test/support";
|
||||
import { serialize } from "./index";
|
||||
|
||||
it.skip("should allow reading only", async () => {
|
||||
await setSelfHosted();
|
||||
describe.skip("policies/team", () => {
|
||||
it("should allow reading only", async () => {
|
||||
setSelfHosted();
|
||||
|
||||
const team = await buildTeam();
|
||||
const user = await buildUser({
|
||||
teamId: team.id,
|
||||
const team = await buildTeam();
|
||||
const user = await buildUser({
|
||||
teamId: team.id,
|
||||
});
|
||||
const abilities = serialize(user, team);
|
||||
expect(abilities.read).toEqual(true);
|
||||
expect(abilities.createTeam).toEqual(false);
|
||||
expect(abilities.createAttachment).toEqual(true);
|
||||
expect(abilities.createCollection).toEqual(true);
|
||||
expect(abilities.createDocument).toEqual(true);
|
||||
expect(abilities.createGroup).toEqual(false);
|
||||
expect(abilities.createIntegration).toEqual(false);
|
||||
});
|
||||
const abilities = serialize(user, team);
|
||||
expect(abilities.read).toEqual(true);
|
||||
expect(abilities.createTeam).toEqual(false);
|
||||
expect(abilities.createAttachment).toEqual(true);
|
||||
expect(abilities.createCollection).toEqual(true);
|
||||
expect(abilities.createDocument).toEqual(true);
|
||||
expect(abilities.createGroup).toEqual(false);
|
||||
expect(abilities.createIntegration).toEqual(false);
|
||||
});
|
||||
|
||||
it.skip("should allow admins to manage", async () => {
|
||||
await setSelfHosted();
|
||||
it("should allow admins to manage", async () => {
|
||||
setSelfHosted();
|
||||
|
||||
const team = await buildTeam();
|
||||
const admin = await buildAdmin({
|
||||
teamId: team.id,
|
||||
const team = await buildTeam();
|
||||
const admin = await buildAdmin({
|
||||
teamId: team.id,
|
||||
});
|
||||
const abilities = serialize(admin, team);
|
||||
expect(abilities.read).toEqual(true);
|
||||
expect(abilities.createTeam).toEqual(false);
|
||||
expect(abilities.createAttachment).toEqual(true);
|
||||
expect(abilities.createCollection).toEqual(true);
|
||||
expect(abilities.createDocument).toEqual(true);
|
||||
expect(abilities.createGroup).toEqual(true);
|
||||
expect(abilities.createIntegration).toEqual(true);
|
||||
});
|
||||
const abilities = serialize(admin, team);
|
||||
expect(abilities.read).toEqual(true);
|
||||
expect(abilities.createTeam).toEqual(false);
|
||||
expect(abilities.createAttachment).toEqual(true);
|
||||
expect(abilities.createCollection).toEqual(true);
|
||||
expect(abilities.createDocument).toEqual(true);
|
||||
expect(abilities.createGroup).toEqual(true);
|
||||
expect(abilities.createIntegration).toEqual(true);
|
||||
});
|
||||
|
||||
it("should allow creation on hosted envs", async () => {
|
||||
setCloudHosted();
|
||||
|
||||
const team = await buildTeam();
|
||||
const admin = await buildAdmin({
|
||||
teamId: team.id,
|
||||
it("should allow creation on hosted envs", async () => {
|
||||
const team = await buildTeam();
|
||||
const admin = await buildAdmin({
|
||||
teamId: team.id,
|
||||
});
|
||||
const abilities = serialize(admin, team);
|
||||
expect(abilities.read).toEqual(true);
|
||||
expect(abilities.createTeam).toEqual(true);
|
||||
expect(abilities.createAttachment).toEqual(true);
|
||||
expect(abilities.createCollection).toEqual(true);
|
||||
expect(abilities.createDocument).toEqual(true);
|
||||
expect(abilities.createGroup).toEqual(true);
|
||||
expect(abilities.createIntegration).toEqual(true);
|
||||
});
|
||||
const abilities = serialize(admin, team);
|
||||
expect(abilities.read).toEqual(true);
|
||||
expect(abilities.createTeam).toEqual(true);
|
||||
expect(abilities.createAttachment).toEqual(true);
|
||||
expect(abilities.createCollection).toEqual(true);
|
||||
expect(abilities.createDocument).toEqual(true);
|
||||
expect(abilities.createGroup).toEqual(true);
|
||||
expect(abilities.createIntegration).toEqual(true);
|
||||
});
|
||||
|
||||
@@ -14,7 +14,9 @@ allow(User, "share", Team, (user, team) => {
|
||||
|
||||
allow(User, "createTeam", Team, () => {
|
||||
if (!env.isCloudHosted) {
|
||||
throw IncorrectEditionError("Functionality is only available on cloud");
|
||||
throw IncorrectEditionError(
|
||||
"Functionality is not available in this edition"
|
||||
);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@@ -28,7 +30,9 @@ allow(User, "update", Team, (user, team) => {
|
||||
|
||||
allow(User, ["delete", "audit"], Team, (user, team) => {
|
||||
if (!env.isCloudHosted) {
|
||||
throw IncorrectEditionError("Functionality is only available on cloud");
|
||||
throw IncorrectEditionError(
|
||||
"Functionality is not available in this edition"
|
||||
);
|
||||
}
|
||||
if (!team || user.isViewer || user.teamId !== team.id) {
|
||||
return false;
|
||||
|
||||
@@ -26,7 +26,7 @@ describe("ImportMarkdownZipTask", () => {
|
||||
expect(response.collections.size).toEqual(1);
|
||||
expect(response.documents.size).toEqual(8);
|
||||
expect(response.attachments.size).toEqual(6);
|
||||
});
|
||||
}, 10000);
|
||||
|
||||
it("should throw an error with corrupt zip", async () => {
|
||||
const fileOperation = await buildFileOperation();
|
||||
|
||||
@@ -4,7 +4,7 @@ import { buildInvite } from "@server/test/factories";
|
||||
import InviteReminderTask from "./InviteReminderTask";
|
||||
|
||||
describe("InviteReminderTask", () => {
|
||||
it("should not destroy documents not deleted", async () => {
|
||||
it("should send reminder emails", async () => {
|
||||
const spy = jest.spyOn(InviteReminderEmail.prototype, "schedule");
|
||||
|
||||
// too old
|
||||
|
||||
@@ -36,8 +36,10 @@ describe("#attachments.create", () => {
|
||||
expect(res.status).toEqual(200);
|
||||
|
||||
const body = await res.json();
|
||||
const attachment = await Attachment.findByPk(body.data.attachment.id);
|
||||
expect(attachment!.expiresAt).toBeNull();
|
||||
const attachment = await Attachment.findByPk(body.data.attachment.id, {
|
||||
rejectOnEmpty: true,
|
||||
});
|
||||
expect(attachment.expiresAt).toBeNull();
|
||||
});
|
||||
|
||||
it("should allow attachment creation for documents", async () => {
|
||||
@@ -71,8 +73,10 @@ describe("#attachments.create", () => {
|
||||
expect(res.status).toEqual(200);
|
||||
|
||||
const body = await res.json();
|
||||
const attachment = await Attachment.findByPk(body.data.attachment.id);
|
||||
expect(attachment!.expiresAt).toBeTruthy();
|
||||
const attachment = await Attachment.findByPk(body.data.attachment.id, {
|
||||
rejectOnEmpty: true,
|
||||
});
|
||||
expect(attachment.expiresAt).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should not allow attachment creation for other documents", async () => {
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { faker } from "@faker-js/faker";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { buildUser, buildTeam } from "@server/test/factories";
|
||||
import {
|
||||
getTestServer,
|
||||
setCloudHosted,
|
||||
setSelfHosted,
|
||||
} from "@server/test/support";
|
||||
import { getTestServer, setSelfHosted } from "@server/test/support";
|
||||
|
||||
const mockTeamInSessionId = uuidv4();
|
||||
|
||||
@@ -18,8 +14,6 @@ jest.mock("@server/utils/authentication", () => ({
|
||||
const server = getTestServer();
|
||||
|
||||
describe("#auth.info", () => {
|
||||
beforeEach(setCloudHosted);
|
||||
|
||||
it("should return current authentication", async () => {
|
||||
const team = await buildTeam();
|
||||
const team2 = await buildTeam();
|
||||
|
||||
@@ -27,7 +27,9 @@ router.post("auth.config", async (ctx: APIContext<T.AuthConfigReq>) => {
|
||||
// brand for the knowledge base and it's guest signin option is used for the
|
||||
// root login page.
|
||||
if (!env.isCloudHosted) {
|
||||
const team = await Team.scope("withAuthenticationProviders").findOne();
|
||||
const team = await Team.scope("withAuthenticationProviders").findOne({
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
|
||||
if (team) {
|
||||
ctx.body = {
|
||||
|
||||
@@ -5,13 +5,11 @@ import {
|
||||
buildEvent,
|
||||
buildUser,
|
||||
} from "@server/test/factories";
|
||||
import { getTestServer, setCloudHosted } from "@server/test/support";
|
||||
import { getTestServer } from "@server/test/support";
|
||||
|
||||
const server = getTestServer();
|
||||
|
||||
describe("#events.list", () => {
|
||||
beforeEach(setCloudHosted);
|
||||
|
||||
it("should only return activity events", async () => {
|
||||
const user = await buildUser();
|
||||
const admin = await buildAdmin({ teamId: user.teamId });
|
||||
|
||||
@@ -6,34 +6,28 @@ import {
|
||||
buildTeam,
|
||||
buildUser,
|
||||
} from "@server/test/factories";
|
||||
import {
|
||||
getTestServer,
|
||||
setCloudHosted,
|
||||
setSelfHosted,
|
||||
} from "@server/test/support";
|
||||
import { getTestServer, setSelfHosted } from "@server/test/support";
|
||||
|
||||
const server = getTestServer();
|
||||
|
||||
describe("teams.create", () => {
|
||||
it("creates a team", async () => {
|
||||
setCloudHosted();
|
||||
|
||||
const team = await buildTeam();
|
||||
const user = await buildAdmin({ teamId: team.id });
|
||||
const name = faker.company.name();
|
||||
const res = await server.post("/api/teams.create", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
name: "factory inc",
|
||||
name,
|
||||
},
|
||||
});
|
||||
const body = await res.json();
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.team.name).toEqual("factory inc");
|
||||
expect(body.data.team.subdomain).toEqual("factory-inc");
|
||||
expect(body.data.team.name).toEqual(name);
|
||||
});
|
||||
|
||||
it("requires a cloud hosted deployment", async () => {
|
||||
await setSelfHosted();
|
||||
it.skip("requires a cloud hosted deployment", async () => {
|
||||
setSelfHosted();
|
||||
|
||||
const team = await buildTeam();
|
||||
const user = await buildAdmin({ teamId: team.id });
|
||||
|
||||
@@ -4,11 +4,9 @@ import { buildDocument, buildUser } from "@server/test/factories";
|
||||
import { getTestServer } from "@server/test/support";
|
||||
import resolvers from "@server/utils/unfurl";
|
||||
|
||||
jest.mock("@server/utils/unfurl", () => ({
|
||||
Iframely: {
|
||||
unfurl: jest.fn(),
|
||||
},
|
||||
}));
|
||||
jest
|
||||
.spyOn(resolvers.Iframely, "unfurl")
|
||||
.mockImplementation(async (_: string) => false);
|
||||
|
||||
const server = getTestServer();
|
||||
|
||||
@@ -166,6 +164,7 @@ describe("#urls.unfurl", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
const body = await res.json();
|
||||
|
||||
expect(resolvers.Iframely.unfurl).toHaveBeenCalledWith(
|
||||
|
||||
@@ -18,6 +18,8 @@ env.OIDC_USERINFO_URI = "http://localhost/userinfo";
|
||||
|
||||
env.RATE_LIMITER_ENABLED = false;
|
||||
|
||||
env.IFRAMELY_API_KEY = "123";
|
||||
|
||||
if (process.env.DATABASE_URL_TEST) {
|
||||
env.DATABASE_URL = process.env.DATABASE_URL_TEST;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { faker } from "@faker-js/faker";
|
||||
import isNil from "lodash/isNil";
|
||||
import isNull from "lodash/isNull";
|
||||
import randomstring from "randomstring";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import {
|
||||
CollectionPermission,
|
||||
@@ -23,7 +24,6 @@ import {
|
||||
Attachment,
|
||||
IntegrationAuthentication,
|
||||
Integration,
|
||||
AuthenticationProvider,
|
||||
FileOperation,
|
||||
WebhookSubscription,
|
||||
WebhookDelivery,
|
||||
@@ -77,7 +77,9 @@ export async function buildStar(overrides: Partial<Star> = {}) {
|
||||
let user;
|
||||
|
||||
if (overrides.userId) {
|
||||
user = await User.findByPk(overrides.userId);
|
||||
user = await User.findByPk(overrides.userId, {
|
||||
rejectOnEmpty: true,
|
||||
});
|
||||
} else {
|
||||
user = await buildUser();
|
||||
overrides.userId = user.id;
|
||||
@@ -86,7 +88,7 @@ export async function buildStar(overrides: Partial<Star> = {}) {
|
||||
if (!overrides.documentId) {
|
||||
const document = await buildDocument({
|
||||
createdById: overrides.userId,
|
||||
teamId: user?.teamId,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
overrides.documentId = document.id;
|
||||
}
|
||||
@@ -101,7 +103,9 @@ export async function buildSubscription(overrides: Partial<Subscription> = {}) {
|
||||
let user;
|
||||
|
||||
if (overrides.userId) {
|
||||
user = await User.findByPk(overrides.userId);
|
||||
user = await User.findByPk(overrides.userId, {
|
||||
rejectOnEmpty: true,
|
||||
});
|
||||
} else {
|
||||
user = await buildUser();
|
||||
overrides.userId = user.id;
|
||||
@@ -110,7 +114,7 @@ export async function buildSubscription(overrides: Partial<Subscription> = {}) {
|
||||
if (!overrides.documentId) {
|
||||
const document = await buildDocument({
|
||||
createdById: overrides.userId,
|
||||
teamId: user?.teamId,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
overrides.documentId = document.id;
|
||||
}
|
||||
@@ -129,7 +133,7 @@ export function buildTeam(overrides: Record<string, any> = {}) {
|
||||
authenticationProviders: [
|
||||
{
|
||||
name: "slack",
|
||||
providerId: uuidv4().replace(/-/g, ""),
|
||||
providerId: randomstring.generate(32),
|
||||
},
|
||||
],
|
||||
...overrides,
|
||||
@@ -170,14 +174,14 @@ export async function buildUser(overrides: Partial<User> = {}) {
|
||||
team = await buildTeam();
|
||||
overrides.teamId = team.id;
|
||||
} else {
|
||||
team = await Team.findByPk(overrides.teamId);
|
||||
team = await Team.findByPk(overrides.teamId, {
|
||||
include: "authenticationProviders",
|
||||
rejectOnEmpty: true,
|
||||
paranoid: false,
|
||||
});
|
||||
}
|
||||
|
||||
const authenticationProvider = await AuthenticationProvider.findOne({
|
||||
where: {
|
||||
teamId: overrides.teamId,
|
||||
},
|
||||
});
|
||||
const authenticationProvider = team.authenticationProviders[0];
|
||||
const user = await User.create(
|
||||
{
|
||||
email: faker.internet.email().toLowerCase(),
|
||||
@@ -185,12 +189,14 @@ export async function buildUser(overrides: Partial<User> = {}) {
|
||||
createdAt: new Date("2018-01-01T00:00:00.000Z"),
|
||||
updatedAt: new Date("2018-01-02T00:00:00.000Z"),
|
||||
lastActiveAt: new Date("2018-01-03T00:00:00.000Z"),
|
||||
authentications: [
|
||||
{
|
||||
authenticationProviderId: authenticationProvider!.id,
|
||||
providerId: uuidv4().replace(/-/g, ""),
|
||||
},
|
||||
],
|
||||
authentications: authenticationProvider
|
||||
? [
|
||||
{
|
||||
authenticationProviderId: authenticationProvider.id,
|
||||
providerId: randomstring.generate(32),
|
||||
},
|
||||
]
|
||||
: [],
|
||||
...overrides,
|
||||
},
|
||||
{
|
||||
@@ -244,7 +250,7 @@ export async function buildIntegration(overrides: Partial<Integration> = {}) {
|
||||
service: IntegrationService.Slack,
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
token: "fake-access-token",
|
||||
token: randomstring.generate(32),
|
||||
scopes: ["example", "scopes", "here"],
|
||||
});
|
||||
return Integration.create({
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import sharedEnv from "@shared/env";
|
||||
import env from "@server/env";
|
||||
import Redis from "@server/storage/redis";
|
||||
|
||||
require("@server/storage/database");
|
||||
@@ -22,3 +24,7 @@ jest.mock("aws-sdk", () => {
|
||||
});
|
||||
|
||||
afterAll(() => Redis.defaultClient.disconnect());
|
||||
|
||||
beforeEach(() => {
|
||||
env.URL = sharedEnv.URL = "https://app.outline.dev";
|
||||
});
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { faker } from "@faker-js/faker";
|
||||
import TestServer from "fetch-test-server";
|
||||
import { WhereOptions } from "sequelize";
|
||||
import sharedEnv from "@shared/env";
|
||||
import env from "@server/env";
|
||||
import { Event, Team } from "@server/models";
|
||||
import onerror from "@server/onerror";
|
||||
import webService from "@server/services/web";
|
||||
import { sequelize } from "@server/storage/database";
|
||||
@@ -18,32 +17,13 @@ export function getTestServer() {
|
||||
};
|
||||
|
||||
afterAll(server.disconnect);
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the environment to be cloud hosted.
|
||||
*/
|
||||
export function setCloudHosted() {
|
||||
return (env.URL = sharedEnv.URL = "https://app.outline.dev");
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the environment to be self hosted.
|
||||
*/
|
||||
export async function setSelfHosted() {
|
||||
env.URL = sharedEnv.URL = "https://wiki.example.com";
|
||||
|
||||
// Self hosted deployments only have one team, to ensure behavior is correct
|
||||
// we need to delete all teams before running tests
|
||||
return Team.destroy({
|
||||
truncate: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function findLatestEvent(where: WhereOptions<Event> = {}) {
|
||||
return Event.findOne({
|
||||
where,
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
export function setSelfHosted() {
|
||||
env.URL = sharedEnv.URL = `https://${faker.internet.domainName()}`;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,11 @@ export async function getTeamFromContext(ctx: Context) {
|
||||
|
||||
let team;
|
||||
if (!env.isCloudHosted) {
|
||||
team = await Team.findOne();
|
||||
if (env.ENVIRONMENT === "test") {
|
||||
team = await Team.findOne({ where: { domain: env.URL } });
|
||||
} else {
|
||||
team = await Team.findOne();
|
||||
}
|
||||
} else if (domain.custom) {
|
||||
team = await Team.findOne({ where: { domain: domain.host } });
|
||||
} else if (domain.teamSubdomain) {
|
||||
|
||||
@@ -1,41 +1,42 @@
|
||||
import { existsSync } from "fs";
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import path from "path";
|
||||
import glob from "glob";
|
||||
import startCase from "lodash/startCase";
|
||||
import env from "@server/env";
|
||||
import Logger from "@server/logging/Logger";
|
||||
import { UnfurlResolver } from "@server/types";
|
||||
|
||||
const rootDir = env.ENVIRONMENT === "test" ? "" : "build";
|
||||
|
||||
const hasResolver = (plugin: string) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const config = require(path.join(process.cwd(), plugin, "plugin.json"));
|
||||
|
||||
return (
|
||||
existsSync(resolverPath(plugin)) &&
|
||||
(config.requiredEnvVars ?? []).every((name: string) => !!env[name])
|
||||
);
|
||||
};
|
||||
|
||||
const resolverPath = (plugin: string) =>
|
||||
path.join(plugin, "server", "unfurl.js");
|
||||
|
||||
const plugins = glob.sync(path.join(rootDir, "plugins/*"));
|
||||
const resolvers: Record<string, UnfurlResolver> = plugins
|
||||
.filter(hasResolver)
|
||||
.map(resolverPath)
|
||||
.reduce((resolvers, resolverPath) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const plugins = glob.sync(path.join(rootDir, "plugins/*/server/unfurl.[jt]s"));
|
||||
const resolvers: Record<string, UnfurlResolver> = plugins.reduce(
|
||||
(resolvers, filePath) => {
|
||||
const resolver: UnfurlResolver = require(path.join(
|
||||
process.cwd(),
|
||||
resolverPath
|
||||
filePath
|
||||
));
|
||||
const name = startCase(resolverPath.split("/")[2]);
|
||||
resolvers[name] = resolver;
|
||||
Logger.debug("utils", `Registered unfurl resolver ${resolverPath}`);
|
||||
const id = filePath.replace("build/", "").split("/")[1];
|
||||
const config = require(path.join(
|
||||
process.cwd(),
|
||||
rootDir,
|
||||
"plugins",
|
||||
id,
|
||||
"plugin.json"
|
||||
));
|
||||
|
||||
// Test the all required env vars are set for the resolver
|
||||
const enabled = (config.requiredEnvVars ?? []).every(
|
||||
(name: string) => !!env[name]
|
||||
);
|
||||
if (!enabled) {
|
||||
return resolvers;
|
||||
}
|
||||
|
||||
resolvers[config.name] = resolver;
|
||||
Logger.debug("utils", `Registered unfurl resolver ${filePath}`);
|
||||
|
||||
return resolvers;
|
||||
}, {});
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
export default resolvers;
|
||||
|
||||
Reference in New Issue
Block a user