## Context - Removing search* integration tests instead of fixing them because they will be replaced by global search very soon - Fixed billing + add missing seeds to make them work - Fixed integration tests not using consistently the correct "test" db - Fixed ci not running the with-db-reset configuration due to nx configuration being used twice for different level of the command - Enriched .env.test - Fixed parts where exceptions were not thrown properly and not caught by exception handler to convert to 400 when needed - Refactored feature flag service that had 2 different implementations in lab and admin panel + added tests - Fixed race condition when migrations are created at the same timestamp and doing the same type of operation, in this case object deletion could break because table could be deleted earlier than its relations - Fixed many integration tests that were not up to date since the CI has been broken for a while --------- Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
534 lines
16 KiB
TypeScript
534 lines
16 KiB
TypeScript
import gql from 'graphql-tag';
|
|
import request from 'supertest';
|
|
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
|
import { updateFeatureFlagFactory } from 'test/integration/graphql/utils/update-feature-flag-factory.util';
|
|
|
|
import { SEED_APPLE_WORKSPACE_ID } from 'src/database/typeorm-seeds/core/workspaces';
|
|
import { BillingPlanKey } from 'src/engine/core-modules/billing/enums/billing-plan-key.enum';
|
|
import { ErrorCode } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
|
import { PermissionsExceptionMessage } from 'src/engine/metadata-modules/permissions/permissions.exception';
|
|
|
|
const client = request(`http://localhost:${APP_PORT}`);
|
|
|
|
describe('workspace permissions', () => {
|
|
let originalWorkspaceState;
|
|
|
|
beforeAll(async () => {
|
|
// Store original workspace state
|
|
const query = gql`
|
|
query getWorkspace {
|
|
currentWorkspace {
|
|
displayName
|
|
isGoogleAuthEnabled
|
|
isMicrosoftAuthEnabled
|
|
isPasswordAuthEnabled
|
|
logo
|
|
isPublicInviteLinkEnabled
|
|
subdomain
|
|
isCustomDomainEnabled
|
|
}
|
|
}
|
|
`;
|
|
|
|
const response = await makeGraphqlAPIRequest({ query });
|
|
|
|
originalWorkspaceState = response.body.data.currentWorkspace;
|
|
|
|
const enablePermissionsQuery = updateFeatureFlagFactory(
|
|
SEED_APPLE_WORKSPACE_ID,
|
|
'IsPermissionsEnabled',
|
|
true,
|
|
);
|
|
|
|
await makeGraphqlAPIRequest(enablePermissionsQuery);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
const disablePermissionsQuery = updateFeatureFlagFactory(
|
|
SEED_APPLE_WORKSPACE_ID,
|
|
'IsPermissionsEnabled',
|
|
false,
|
|
);
|
|
|
|
await makeGraphqlAPIRequest(disablePermissionsQuery);
|
|
|
|
// Restore workspace state
|
|
const restoreQuery = gql`
|
|
mutation updateWorkspace {
|
|
updateWorkspace(data: {
|
|
displayName: "${originalWorkspaceState.displayName}",
|
|
subdomain: "${originalWorkspaceState.subdomain}",
|
|
logo: "${originalWorkspaceState.logo}",
|
|
isGoogleAuthEnabled: ${originalWorkspaceState.isGoogleAuthEnabled},
|
|
isMicrosoftAuthEnabled: ${originalWorkspaceState.isMicrosoftAuthEnabled},
|
|
isPasswordAuthEnabled: ${originalWorkspaceState.isPasswordAuthEnabled}
|
|
isPublicInviteLinkEnabled: ${originalWorkspaceState.isPublicInviteLinkEnabled}
|
|
}) {
|
|
id
|
|
}
|
|
}
|
|
`;
|
|
|
|
await makeGraphqlAPIRequest({ query: restoreQuery });
|
|
});
|
|
|
|
describe('workspace permissions', () => {
|
|
describe('delete workspace', () => {
|
|
it('should throw a permission error when user does not have permission (member role)', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation DeleteCurrentWorkspace {
|
|
deleteCurrentWorkspace {
|
|
id
|
|
__typename
|
|
}
|
|
}
|
|
`,
|
|
};
|
|
|
|
await client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeNull();
|
|
expect(res.body.errors).toBeDefined();
|
|
expect(res.body.errors[0].message).toBe(
|
|
PermissionsExceptionMessage.PERMISSION_DENIED,
|
|
);
|
|
expect(res.body.errors[0].extensions.code).toBe(
|
|
ErrorCode.FORBIDDEN,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
describe('display name update', () => {
|
|
it('should update workspace display name when user has workspace settings permission', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation updateWorkspace {
|
|
updateWorkspace(data: { displayName: "New Workspace Name" }) {
|
|
id
|
|
displayName
|
|
}
|
|
}
|
|
`,
|
|
};
|
|
|
|
return client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeDefined();
|
|
expect(res.body.errors).toBeUndefined();
|
|
})
|
|
.expect((res) => {
|
|
const data = res.body.data.updateWorkspace;
|
|
|
|
expect(data).toBeDefined();
|
|
expect(data.displayName).toBe('New Workspace Name');
|
|
});
|
|
});
|
|
|
|
it('should throw a permission error when user does not have permission (member role)', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation updateWorkspace {
|
|
updateWorkspace(data: { displayName: "Another New Workspace Name" }) {
|
|
id
|
|
displayName
|
|
}
|
|
}
|
|
`,
|
|
};
|
|
|
|
await client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeNull();
|
|
expect(res.body.errors).toBeDefined();
|
|
expect(res.body.errors[0].message).toBe(
|
|
PermissionsExceptionMessage.PERMISSION_DENIED,
|
|
);
|
|
expect(res.body.errors[0].extensions.code).toBe(
|
|
ErrorCode.FORBIDDEN,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('subdomain update', () => {
|
|
it('should update workspace subdomain when user has workspace settings permission', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation updateWorkspace {
|
|
updateWorkspace(data: { subdomain: "new-subdomain" }) {
|
|
id
|
|
subdomain
|
|
}
|
|
}
|
|
`,
|
|
};
|
|
|
|
return client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeDefined();
|
|
expect(res.body.errors).toBeUndefined();
|
|
})
|
|
.expect((res) => {
|
|
const data = res.body.data.updateWorkspace;
|
|
|
|
expect(data).toBeDefined();
|
|
expect(data.subdomain).toBe('new-subdomain');
|
|
});
|
|
});
|
|
|
|
it('should throw a permission error when user does not have permission (member role)', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation updateWorkspace {
|
|
updateWorkspace(data: { subdomain: "another-new-subdomain" }) {
|
|
id
|
|
subdomain
|
|
}
|
|
}
|
|
`,
|
|
};
|
|
|
|
await client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeNull();
|
|
expect(res.body.errors).toBeDefined();
|
|
expect(res.body.errors[0].message).toBe(
|
|
PermissionsExceptionMessage.PERMISSION_DENIED,
|
|
);
|
|
expect(res.body.errors[0].extensions.code).toBe(
|
|
ErrorCode.FORBIDDEN,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('custom domain update', () => {
|
|
it('should update workspace custom domain when user has workspace settings permission', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation updateWorkspace {
|
|
updateWorkspace(data: { customDomain: null }) {
|
|
id
|
|
customDomain
|
|
}
|
|
}
|
|
`,
|
|
};
|
|
|
|
return client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeDefined();
|
|
expect(res.body.errors).toBeUndefined();
|
|
})
|
|
.expect((res) => {
|
|
const data = res.body.data.updateWorkspace;
|
|
|
|
expect(data).toBeDefined();
|
|
expect(data.customDomain).toBe(null);
|
|
});
|
|
});
|
|
|
|
it('should throw a permission error when user does not have permission (member role)', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation updateWorkspace {
|
|
updateWorkspace(data: { customDomain: "another-new-custom-domain" }) {
|
|
id
|
|
customDomain
|
|
}
|
|
}
|
|
`,
|
|
};
|
|
|
|
await client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeNull();
|
|
expect(res.body.errors).toBeDefined();
|
|
expect(res.body.errors[0].message).toBe(
|
|
PermissionsExceptionMessage.PERMISSION_DENIED,
|
|
);
|
|
expect(res.body.errors[0].extensions.code).toBe(
|
|
ErrorCode.FORBIDDEN,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('logo update', () => {
|
|
it('should update workspace logo when user has workspace settings permission', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation updateWorkspace {
|
|
updateWorkspace(data: { logo: "new-logo" }) {
|
|
id
|
|
logo
|
|
}
|
|
}
|
|
`,
|
|
};
|
|
|
|
return client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeDefined();
|
|
expect(res.body.errors).toBeUndefined();
|
|
})
|
|
.expect((res) => {
|
|
const data = res.body.data.updateWorkspace;
|
|
|
|
expect(data).toBeDefined();
|
|
expect(data.logo).toContain('new-logo');
|
|
});
|
|
});
|
|
|
|
it('should throw a permission error when user does not have permission (member role)', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation updateWorkspace {
|
|
updateWorkspace(data: { logo: "another-new-logo" }) {
|
|
id
|
|
logo
|
|
}
|
|
}
|
|
`,
|
|
};
|
|
|
|
await client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeNull();
|
|
expect(res.body.errors).toBeDefined();
|
|
expect(res.body.errors[0].message).toBe(
|
|
PermissionsExceptionMessage.PERMISSION_DENIED,
|
|
);
|
|
expect(res.body.errors[0].extensions.code).toBe(
|
|
ErrorCode.FORBIDDEN,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('billing', () => {
|
|
describe('updateBillingSubscription', () => {
|
|
it('should throw a permission error when user does not have permission (member role)', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation UpdateBillingSubscription {
|
|
updateBillingSubscription {
|
|
success
|
|
}
|
|
}
|
|
`,
|
|
};
|
|
|
|
await client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeNull();
|
|
expect(res.body.errors).toBeDefined();
|
|
expect(res.body.errors[0].message).toBe(
|
|
PermissionsExceptionMessage.PERMISSION_DENIED,
|
|
);
|
|
expect(res.body.errors[0].extensions.code).toBe(
|
|
ErrorCode.FORBIDDEN,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('billingPortalSession', () => {
|
|
it('should throw a permission error when user does not have permission (member role)', async () => {
|
|
const queryData = {
|
|
query: `
|
|
query BillingPortalSession($returnUrlPath: String!) {
|
|
billingPortalSession(returnUrlPath: $returnUrlPath) {
|
|
url
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
returnUrlPath: '/settings/billing',
|
|
},
|
|
};
|
|
|
|
await client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeNull();
|
|
expect(res.body.errors).toBeDefined();
|
|
expect(res.body.errors[0].message).toBe(
|
|
PermissionsExceptionMessage.PERMISSION_DENIED,
|
|
);
|
|
expect(res.body.errors[0].extensions.code).toBe(
|
|
ErrorCode.FORBIDDEN,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('checkoutSession', () => {
|
|
it('should throw a permission error when user does not have permission (member role)', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation CheckoutSession(
|
|
$recurringInterval: SubscriptionInterval!
|
|
$successUrlPath: String!
|
|
$plan: BillingPlanKey!
|
|
$requirePaymentMethod: Boolean
|
|
) {
|
|
checkoutSession(
|
|
recurringInterval: $recurringInterval
|
|
successUrlPath: $successUrlPath
|
|
plan: $plan
|
|
requirePaymentMethod: $requirePaymentMethod
|
|
) {
|
|
url
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
recurringInterval: 'Month',
|
|
successUrlPath: '/settings/billing',
|
|
plan: BillingPlanKey.PRO,
|
|
requirePaymentMethod: true,
|
|
},
|
|
};
|
|
|
|
await client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeNull();
|
|
expect(res.body.errors).toBeDefined();
|
|
expect(res.body.errors[0].message).toBe(
|
|
PermissionsExceptionMessage.PERMISSION_DENIED,
|
|
);
|
|
expect(res.body.errors[0].extensions.code).toBe(
|
|
ErrorCode.FORBIDDEN,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('lab', () => {
|
|
describe('updateLabPublicFeatureFlag', () => {
|
|
it('should update feature flag when user has workspace settings permission', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation UpdateLabPublicFeatureFlag(
|
|
$input: UpdateLabPublicFeatureFlagInput!
|
|
) {
|
|
updateLabPublicFeatureFlag(input: $input) {
|
|
id
|
|
key
|
|
value
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
input: {
|
|
publicFeatureFlag: 'TestFeature',
|
|
value: true,
|
|
},
|
|
},
|
|
};
|
|
|
|
await client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${ADMIN_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeDefined();
|
|
expect(res.body.errors).toBeDefined();
|
|
expect(res.body.errors[0].message).toBe(
|
|
'Invalid feature flag key, flag is not public',
|
|
);
|
|
});
|
|
});
|
|
|
|
it('should throw a permission error when user does not have permission (member role)', async () => {
|
|
const queryData = {
|
|
query: `
|
|
mutation UpdateLabPublicFeatureFlag(
|
|
$input: UpdateLabPublicFeatureFlagInput!
|
|
) {
|
|
updateLabPublicFeatureFlag(input: $input) {
|
|
id
|
|
key
|
|
value
|
|
}
|
|
}
|
|
`,
|
|
variables: {
|
|
input: {
|
|
publicFeatureFlag: 'TestFeature',
|
|
value: false,
|
|
},
|
|
},
|
|
};
|
|
|
|
await client
|
|
.post('/graphql')
|
|
.set('Authorization', `Bearer ${MEMBER_ACCESS_TOKEN}`)
|
|
.send(queryData)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body.data).toBeNull();
|
|
expect(res.body.errors).toBeDefined();
|
|
expect(res.body.errors[0].message).toBe(
|
|
PermissionsExceptionMessage.PERMISSION_DENIED,
|
|
);
|
|
expect(res.body.errors[0].extensions.code).toBe(
|
|
ErrorCode.FORBIDDEN,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|