mirror of
https://github.com/lingble/twenty.git
synced 2025-10-29 03:42:30 +00:00
feat: add integration tests (#6923)
### Summary This PR introduces several integration tests, a mix of manually written tests and those generated using the `generate-integration-tests` Python script located in the `scripts` folder. ### Tests Added: - **Authentication tests**: Validating login, registration, and token handling. - **FindMany queries**: Fetching multiple records for all existing entities that do not require input arguments. ### How the Integration Tests Work: - A `setupTest` function is called during the Jest test run. This function initializes a test instance of the application and exposes it on a dedicated port. - Since tests are executed in isolated workers, they do not have direct access to the in-memory app instance. Instead, the tests query the application through the exposed port. - A static accessToken is used, this one as a big expiration time so it will never expire (365 years) - The queries are executed, and the results are validated against expected outcomes. ### Current State and Next Steps: - These tests currently run using the existing development seed data. We plan to introduce more comprehensive test data using `faker` to improve coverage. - At the moment, the only mutation tests implemented are for authentication. Future updates should include broader mutation testing for other entities. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
75
.github/workflows/ci-server.yaml
vendored
75
.github/workflows/ci-server.yaml
vendored
@@ -17,7 +17,7 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
server-test:
|
||||
server-setup:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
|
||||
@@ -29,6 +29,10 @@ jobs:
|
||||
POSTGRES_USER: postgres
|
||||
ports:
|
||||
- 5432:5432
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
steps:
|
||||
- name: Fetch custom Github Actions and base branch history
|
||||
uses: actions/checkout@v4
|
||||
@@ -36,7 +40,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Install dependencies
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Server / Restore Tasks Cache
|
||||
- name: Server / Restore Task Cache
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:backend
|
||||
@@ -45,14 +49,71 @@ jobs:
|
||||
with:
|
||||
tag: scope:backend
|
||||
tasks: lint,typecheck
|
||||
- name: Server / Run tests
|
||||
uses: ./.github/workflows/actions/nx-affected
|
||||
with:
|
||||
tag: scope:backend
|
||||
tasks: test
|
||||
- name: Server / Build
|
||||
run: npx nx build twenty-server
|
||||
- name: Server / Write .env
|
||||
run: npx nx reset:env twenty-server
|
||||
- name: Worker / Run
|
||||
run: MESSAGE_QUEUE_TYPE=sync npx nx worker twenty-server
|
||||
|
||||
server-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: server-setup
|
||||
env:
|
||||
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
|
||||
steps:
|
||||
- name: Fetch custom Github Actions and base branch history
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install dependencies
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Server / Restore Task Cache
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:backend
|
||||
- name: Server / Run Tests
|
||||
uses: ./.github/workflows/actions/nx-affected
|
||||
with:
|
||||
tag: scope:backend
|
||||
tasks: test
|
||||
|
||||
server-integration-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: server-setup
|
||||
services:
|
||||
postgres:
|
||||
image: twentycrm/twenty-postgres
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
ports:
|
||||
- 5432:5432
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
env:
|
||||
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
|
||||
steps:
|
||||
- name: Fetch custom Github Actions and base branch history
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install dependencies
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Server / Restore Task Cache
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:backend
|
||||
- name: Server / Run Integration Tests
|
||||
uses: ./.github/workflows/actions/nx-affected
|
||||
with:
|
||||
tag: scope:backend
|
||||
tasks: "test:integration"
|
||||
- name: Server / Upload reset-logs file
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: reset-logs
|
||||
path: reset-logs.log
|
||||
|
||||
4
.vscode/twenty.code-workspace
vendored
4
.vscode/twenty.code-workspace
vendored
@@ -20,6 +20,10 @@
|
||||
"name": "packages/twenty-ui",
|
||||
"path": "../packages/twenty-ui"
|
||||
},
|
||||
{
|
||||
"name": "packages/twenty-emails",
|
||||
"path": "../packages/twenty-emails"
|
||||
},
|
||||
{
|
||||
"name": "packages/twenty-postgres",
|
||||
"path": "../packages/twenty-postgres"
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
PG_DATABASE_URL=postgres://twenty:twenty@localhost:5432/test
|
||||
|
||||
DEBUG_MODE=true
|
||||
PG_DATABASE_URL=postgres://twenty:twenty@localhost:5432/test?connection_limit=1
|
||||
# Use this for docker setup
|
||||
# PG_DATABASE_URL=postgres://twenty:twenty@postgres:5432/default?connection_limit=1
|
||||
|
||||
# the URL of the front-end app
|
||||
DEBUG_PORT=9000
|
||||
FRONT_BASE_URL=http://localhost:3001
|
||||
# random keys used to generate JWT tokens
|
||||
ACCESS_TOKEN_SECRET=secret_jwt
|
||||
LOGIN_TOKEN_SECRET=secret_login_tokens
|
||||
REFRESH_TOKEN_SECRET=secret_refresh_token
|
||||
FILE_TOKEN_SECRET=secret_file_token
|
||||
ACCESS_TOKEN_SECRET=replace_me_with_a_random_string_access
|
||||
LOGIN_TOKEN_SECRET=replace_me_with_a_random_string_login
|
||||
REFRESH_TOKEN_SECRET=replace_me_with_a_random_string_refresh
|
||||
SIGN_IN_PREFILLED=true
|
||||
EXCEPTION_HANDLER_DRIVER=console
|
||||
SENTRY_DSN=https://ba869cb8fd72d5faeb6643560939cee0@o4505516959793152.ingest.sentry.io/4506660900306944
|
||||
DEMO_WORKSPACE_IDS=63db4589-590f-42b3-bdf1-85268b3da02f,8de58f3f-7e86-4a0b-998d-b2cbe314ee3a,4d957b72-0b37-4bad-9468-8dc828ee082d,daa0b739-269e-49b6-9be5-5f0215941489,59c15f6a-909a-4495-9cf4-3ce1b0abbb6a,7202cc9d-92da-4b52-a323-d29d38cd3b4e,5f071b0d-646b-411a-94f1-5d9ba9d5c6ac,7bc10973-897b-4767-ab2f-35cdac3b2aec,4b3ba0be-2d29-4b1e-be66-8ac7eb65d000,edfb500d-cc4e-4f22-8e2b-f139a9758a68,eee459c9-9057-4459-ae0d-d51d14c01635,3dd2f505-0075-4217-ba33-fc4244aeaaa9,3d1a9165-3f3f-494e-a99d-f858eae95144,84db6ded-cfce-4aee-9160-6553b05c8143,96fb1540-269b-4d13-af21-2a8268eff8ca,b2463e69-d121-4ea5-80c9-bba82403e93e,5af30c15-867d-49ed-b939-d4856bed8514,b5677aa1-68fa-4818-aaaa-434a07ae2ed4,1ec7fa9a-d6bf-4fa2-a753-9a235d75ee3f,753a6fa2-df27-4c87-8c90-4da78fcb30dd,2138f2f2-bbe9-41df-b483-687a9075f94e,a885cfef-4636-4c3a-9788-1ff6e6b92df5,5458f7fb-9431-47a2-b7a0-32f31d115e23,6c09929f-11c3-4f92-9508-aa0e6b934d1e,57ae0a2c-7a4e-4c7d-8f43-68548e7f1206,cc7f0b85-6868-4c2d-85c5-3ce9977ea346,21871a7f-f067-45ea-989e-44339bb5ad07,c3efedab-84f5-4656-8297-55964b3d26cb,647dcdd1-4540-4003-9f58-fd84d4d759b7,fc5e6857-8d67-47b8-98f2-edeb0671e326,1ad8d72c-1826-40ed-8b44-d15a1d2aab70,eac6c90a-d25d-4c8c-a053-cfbc7cde0afb,023a70de-a85e-43fc-bbc6-757fbf6562f0,f3f0a7fb-1409-443b-8e39-4e58e628796e,62828804-97d4-40ec-82fa-2992a6ce4a81,af5441fe-b16f-4996-87f4-1a433ec53dd6,e8857860-f7b1-4478-9741-1eb9e7c11f2c,6bca9c44-c8c0-49f8-b0b5-1bb2ca7842b8,d50da092-09df-448f-84ea-3ebddfe1d9f6,9efd5d6d-db64-47d4-9ad3-5e4d8b65ff7f,6f089094-2dd2-4b0e-b5b7-8bb52b93ea8e,299b0822-68e9-4bfa-af35-da799012e80e,a3dd579c-93be-45a0-ad35-f518d8ed45dd,023b1b3e-4891-4061-aae0-f34368644f40,50174445-33c5-4482-bb8c-3ef6c511c8cd,9933c048-07a7-4735-9af2-940c2f9b6683,beadc568-3962-46bd-ad4d-06e23b37615b,0cdafc9f-d4c1-4576-837e-d7f6ec28643d,50bb24ce-1709-4928-a87b-d9d9e147a2ab,7690ed72-910d-4357-8e0e-17aa702b0b94,1ad0d69f-60fa-414f-bf79-4f94c2abba43,946d84a4-db4d-48cb-a5d3-03081b5c7e8e,1a080055-d2bf-4b14-8957-88a7d08769b8,ed343e38-e405-4fae-9486-27b09c98bdad,c8bdef75-a98c-4646-a372-3251340d2dea,87a8c6fa-f93e-4950-aff2-5f956ca1a6ba,604781ba-23c2-4220-a717-b5615431fcd9,31af6841-ad9f-4f28-a637-b5c5e6589447,cf067451-7b88-4ff2-a96d-3fc9c5d6fea0,26a8ad5e-29d9-4e7d-aa1f-e6221e8ea32a,fd14db29-e4df-44a7-9b3f-d00384458122,73b477a8-fcf4-4860-a685-65a0a79b8653,82e0f305-4c6c-4160-be1d-b0de834124e6,e38567ab-a6e2-4a94-99c5-a7db31c0aae8,faf3d6dc-66ff-4c1b-9658-f65a9cd9fcf1,6df6bb90-200e-4290-b73d-9bb374554229,2ff10cf4-a871-404a-9e7b-5ca7a232567e,06c614e2-0f36-4b72-8c82-59631680add2,5e508c81-3453-4185-ae8c-4c9b841f8c15,21b5c371-6010-4b1b-be67-7538eb877efb,54e61442-e291-4eea-8d49-7f11b5f85bd2,b6b7260a-4eea-40b0-9f7f-1dfd4c3cc7a8,e163fe76-30fb-44fb-b51a-50cc78745a21,4da672f2-29b4-4a98-b27c-b39a4aecc858,2fdb0601-c882-4aaf-ad49-ae17e530d47a,49525e1b-1b47-4545-a98c-0ba58778179f,f958ab32-b152-4004-9228-18148f7380f1,0ff5025a-62cd-4a10-a722-79f7cf360f01,642df445-e314-409a-a97d-64fc2aa2a15e,38b0dab5-d4fb-44f9-8cf9-bb35cf82e91d,62054133-f35a-4f64-a2ee-a31e48952835,536dbe8c-af33-4eab-a0a8-8d039a00db40,a04998ba-52c9-4538-b6d9-6d04408dbaf2,89016c7a-3d36-4619-a5c6-4f31795eebf7,7708b9a9-776c-46fc-94a4-dc28e7880958,5c92bc69-b328-4c66-a791-a05dbaf7a6f8,ad580a50-80b4-44be-9bc4-f2b57cd23207,36c0241c-891e-4b74-bd10-5e99df96bbc8,a96842ff-18be-4536-a23d-20d973d91621,0ea549b0-9558-4bdf-9944-5abc707c7660,0186c353-5ed2-4c94-b71a-fc0b48c90288,1508a165-2217-4911-b31c-1ea42a08f097,1731e392-dfdf-4fc4-863b-27ae62b0e374,0b245cea-96a6-4a3a-af6a-ef43496c239c,a844e208-7078-43a2-8bd0-86f31498cd3f,53d112b5-87f2-490b-a788-df1f4624f9ad,0d5794d4-3a52-482b-9a6a-f8185018bad1,df877aa6-231c-47fb-9be0-906e61677356,c56c6d1a-3418-49d2-82ce-bd9370668043,6e0b6f34-3cd0-4aa0-ae1f-25f5545dca68
|
||||
MUTATION_MAXIMUM_RECORD_AFFECTED=100
|
||||
MESSAGE_QUEUE_TYPE=pg-boss
|
||||
CACHE_STORAGE_TYPE=redis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PORT=6379
|
||||
REDIS_USERNAME=default
|
||||
REDIS_PASSWORD=
|
||||
|
||||
# ———————— Optional ————————
|
||||
# DEBUG_MODE=false
|
||||
# SIGN_IN_PREFILLED=false
|
||||
# ACCESS_TOKEN_EXPIRES_IN=30m
|
||||
# LOGIN_TOKEN_EXPIRES_IN=15m
|
||||
# REFRESH_TOKEN_EXPIRES_IN=90d
|
||||
# FILE_TOKEN_EXPIRES_IN=1d
|
||||
# FRONT_AUTH_CALLBACK_URL=http://localhost:3001/verify
|
||||
# AUTH_GOOGLE_ENABLED=false
|
||||
# MESSAGING_PROVIDER_GMAIL_ENABLED=false
|
||||
# STORAGE_TYPE=local
|
||||
# STORAGE_LOCAL_PATH=.local-storage
|
||||
# MUTATION_MAXIMUM_AFFECTED_RECORDS=100
|
||||
AUTH_GOOGLE_ENABLED=false
|
||||
MESSAGING_PROVIDER_GMAIL_ENABLED=false
|
||||
CALENDAR_PROVIDER_GOOGLE_ENABLED=false
|
||||
AUTH_GOOGLE_CALLBACK_URL=http://localhost:3000/auth/google/redirect
|
||||
AUTH_GOOGLE_APIS_CALLBACK_URL=http://localhost:3000/auth/google-apis/get-access-token
|
||||
MESSAGING_PROVIDER_GMAIL_CALLBACK_URL=http://localhost:3000/auth/google-gmail/get-access-token
|
||||
|
||||
@@ -93,5 +93,11 @@ module.exports = {
|
||||
'@nx/workspace-inject-workspace-repository': 'warn',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['scripts/**/*.ts'],
|
||||
parserOptions: {
|
||||
project: ['packages/twenty-server/tsconfig.scripts.json'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
1
packages/twenty-server/.gitignore
vendored
1
packages/twenty-server/.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
dist/*
|
||||
.local-storage
|
||||
logs/**/*
|
||||
*.log
|
||||
|
||||
17
packages/twenty-server/@types/jest.d.ts
vendored
Normal file
17
packages/twenty-server/@types/jest.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'jest';
|
||||
|
||||
declare module '@jest/types' {
|
||||
namespace Config {
|
||||
interface ConfigGlobals {
|
||||
APP_PORT: number;
|
||||
ACCESS_TOKEN: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
const APP_PORT: number;
|
||||
const ACCESS_TOKEN: string;
|
||||
}
|
||||
|
||||
export {};
|
||||
34
packages/twenty-server/jest-integration.config.ts
Normal file
34
packages/twenty-server/jest-integration.config.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { JestConfigWithTsJest, pathsToModuleNameMapper } from 'ts-jest';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const tsConfig = require('./tsconfig.json');
|
||||
|
||||
const jestConfig: JestConfigWithTsJest = {
|
||||
silent: false,
|
||||
verbose: true,
|
||||
moduleFileExtensions: ['js', 'json', 'ts'],
|
||||
rootDir: '.',
|
||||
testEnvironment: 'node',
|
||||
testRegex: '.integration-spec.ts$',
|
||||
modulePathIgnorePatterns: ['<rootDir>/dist'],
|
||||
globalSetup: '<rootDir>/test/utils/setup-test.ts',
|
||||
globalTeardown: '<rootDir>/test/utils/teardown-test.ts',
|
||||
testTimeout: 15000,
|
||||
moduleNameMapper: {
|
||||
...pathsToModuleNameMapper(tsConfig.compilerOptions.paths),
|
||||
'twenty-emails': '<rootDir>/../twenty-emails/dist/index.js',
|
||||
},
|
||||
fakeTimers: {
|
||||
enableGlobally: true,
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.(t|j)s$': 'ts-jest',
|
||||
},
|
||||
globals: {
|
||||
APP_PORT: 4000,
|
||||
ACCESS_TOKEN:
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwiaWF0IjoxNzI2NDkyNTAyLCJleHAiOjEzMjQ1MDE2NTAyfQ.zM6TbfeOqYVH5Sgryc2zf02hd9uqUOSL1-iJlMgwzsI',
|
||||
},
|
||||
};
|
||||
|
||||
export default jestConfig;
|
||||
@@ -1,22 +1,27 @@
|
||||
module.exports = {
|
||||
import { JestConfigWithTsJest } from 'ts-jest';
|
||||
|
||||
const jestConfig: JestConfigWithTsJest = {
|
||||
// to enable logs, comment out the following line
|
||||
silent: true,
|
||||
clearMocks: true,
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
modulePathIgnorePatterns: ['<rootDir>/dist'],
|
||||
moduleFileExtensions: ['js', 'json', 'ts'],
|
||||
moduleNameMapper: {
|
||||
'^src/(.*)': '<rootDir>/src/$1',
|
||||
},
|
||||
displayName: 'twenty-server',
|
||||
rootDir: './',
|
||||
testEnvironment: 'node',
|
||||
transformIgnorePatterns: ['../../node_modules/'],
|
||||
testRegex: '.*\\.spec\\.ts$',
|
||||
transform: {
|
||||
'^.+\\.(t|j)s$': 'ts-jest',
|
||||
},
|
||||
moduleNameMapper: {
|
||||
'^src/(.*)': '<rootDir>/src/$1',
|
||||
},
|
||||
moduleFileExtensions: ['js', 'json', 'ts'],
|
||||
modulePathIgnorePatterns: ['<rootDir>/dist'],
|
||||
fakeTimers: {
|
||||
enableGlobally: true,
|
||||
},
|
||||
collectCoverageFrom: ['**/*.(t|j)s'],
|
||||
coverageDirectory: '../coverage',
|
||||
};
|
||||
|
||||
export default jestConfig;
|
||||
|
||||
@@ -11,6 +11,16 @@
|
||||
"commands": ["rimraf dist", "nest build --path ./tsconfig.build.json"]
|
||||
}
|
||||
},
|
||||
"test:integration": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"cwd": "packages/twenty-server",
|
||||
"commands": [
|
||||
"NODE_ENV=test nx database:reset > reset-logs.log && NODE_ENV=test nx jest --config ./jest-integration.config.ts"
|
||||
]
|
||||
},
|
||||
"parallel": false
|
||||
},
|
||||
"build:packageJson": {
|
||||
"executor": "@nx/js:tsc",
|
||||
"options": {
|
||||
@@ -108,12 +118,11 @@
|
||||
"command": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register ../../node_modules/.bin/jest --runInBand"
|
||||
}
|
||||
},
|
||||
"test:e2e": {
|
||||
"jest": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": ["build"],
|
||||
"options": {
|
||||
"cwd": "packages/twenty-server",
|
||||
"command": "./scripts/run-integration.sh"
|
||||
"command": "jest"
|
||||
}
|
||||
},
|
||||
"database:migrate": {
|
||||
@@ -140,6 +149,16 @@
|
||||
"parallel": false
|
||||
}
|
||||
},
|
||||
"generate:integration-test": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"cwd": "packages/twenty-server",
|
||||
"commands": [
|
||||
"nx ts-node-no-deps -- ./scripts/generate-integration-tests/index.ts"
|
||||
],
|
||||
"parallel": false
|
||||
}
|
||||
},
|
||||
"database:reset": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": ["build"],
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as process from 'process';
|
||||
|
||||
import { INTROSPECTION_QUERY } from './introspection-query';
|
||||
import {
|
||||
Field,
|
||||
InputValue,
|
||||
IntrospectionResponse,
|
||||
TypeRef,
|
||||
} from './introspection.interface';
|
||||
|
||||
const GRAPHQL_URL = 'http://localhost:3000/graphql';
|
||||
const BEARER_TOKEN =
|
||||
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC05ZTNiLTQ2ZDQtYTU1Ni04OGI5ZGRjMmIwMzQiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtMDY4Ny00YzQxLWI3MDctZWQxYmZjYTk3MmE3IiwiaWF0IjoxNzI2NDkyNTAyLCJleHAiOjEzMjQ1MDE2NTAyfQ.zM6TbfeOqYVH5Sgryc2zf02hd9uqUOSL1-iJlMgwzsI';
|
||||
const TEST_OUTPUT_DIR = './test';
|
||||
|
||||
const fetchGraphQLSchema = async (): Promise<IntrospectionResponse> => {
|
||||
const headers = {
|
||||
Authorization: BEARER_TOKEN,
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
const response = await fetch(GRAPHQL_URL, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({ query: INTROSPECTION_QUERY }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch schema: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
};
|
||||
|
||||
const toKebabCase = (name: string): string => {
|
||||
return name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
};
|
||||
|
||||
const unwrapType = (typeInfo: TypeRef): any => {
|
||||
while (typeInfo.ofType) {
|
||||
typeInfo = typeInfo.ofType;
|
||||
}
|
||||
|
||||
return typeInfo;
|
||||
};
|
||||
|
||||
const hasRequiredArgs = (args: InputValue[]): boolean => {
|
||||
return args.some((arg) => unwrapType(arg.type).kind === 'NON_NULL');
|
||||
};
|
||||
|
||||
const generateTestContent = (
|
||||
queryName: string,
|
||||
fields: Field[],
|
||||
): string | null => {
|
||||
const fieldNames = fields
|
||||
.filter((f) => ['SCALAR', 'ENUM'].includes(unwrapType(f.type).kind))
|
||||
.map((f) => f.name);
|
||||
|
||||
if (fieldNames.length === 0) {
|
||||
console.log(`Skipping ${queryName}: No usable fields found.`);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const fieldSelection = fieldNames.join('\n ');
|
||||
const expectSelection = fieldNames
|
||||
.map((f) => `expect(${queryName}).toHaveProperty('${f}');`)
|
||||
.join('\n ');
|
||||
|
||||
return `import request from 'supertest';
|
||||
|
||||
const client = request(\`http://localhost:\${APP_PORT}\`);
|
||||
|
||||
describe('${queryName}Resolver (e2e)', () => {
|
||||
it('should find many ${queryName}', () => {
|
||||
const queryData = {
|
||||
query: \`
|
||||
query ${queryName} {
|
||||
${queryName} {
|
||||
edges {
|
||||
node {
|
||||
${fieldSelection}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
\`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', \`Bearer \${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.${queryName};
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const ${queryName} = edges[0].node;
|
||||
|
||||
${expectSelection}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
`;
|
||||
};
|
||||
|
||||
const writeTestFile = (
|
||||
queryName: string,
|
||||
content: string | null,
|
||||
force = false,
|
||||
): string => {
|
||||
if (!content) return 'skipped';
|
||||
|
||||
const fileName = `${toKebabCase(queryName)}.integration-spec.ts`;
|
||||
const filePath = path.join(TEST_OUTPUT_DIR, fileName);
|
||||
|
||||
if (fs.existsSync(filePath) && !force) {
|
||||
return 'skipped';
|
||||
}
|
||||
|
||||
fs.writeFileSync(filePath, content);
|
||||
|
||||
return force ? 'updated' : 'created';
|
||||
};
|
||||
|
||||
const generateTests = async (force = false) => {
|
||||
fs.mkdirSync(TEST_OUTPUT_DIR, { recursive: true });
|
||||
const schemaData = await fetchGraphQLSchema();
|
||||
const types = schemaData.data.__schema.types;
|
||||
|
||||
const queryTypeName = schemaData.data.__schema.queryType.name;
|
||||
const queryType = types.find((t: any) => t.name === queryTypeName);
|
||||
|
||||
let createdCount = 0;
|
||||
let updatedCount = 0;
|
||||
let totalCount = 0;
|
||||
|
||||
if (!queryType?.fields) {
|
||||
console.log('No query fields found.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (const query of queryType.fields) {
|
||||
const queryName = query.name;
|
||||
|
||||
if (hasRequiredArgs(query.args)) continue;
|
||||
if (queryName.includes('Duplicates')) continue;
|
||||
|
||||
const queryReturnType = unwrapType(query.type);
|
||||
|
||||
if (
|
||||
queryReturnType.kind === 'OBJECT' &&
|
||||
queryReturnType.name.includes('Connection')
|
||||
) {
|
||||
totalCount++;
|
||||
const connectionTypeInfo = types.find(
|
||||
(f: any) => f.name === queryReturnType.name,
|
||||
);
|
||||
const edgeTypeInfo = connectionTypeInfo?.fields?.find(
|
||||
(f: any) => f.name === 'edges',
|
||||
);
|
||||
|
||||
if (edgeTypeInfo) {
|
||||
const returnType = unwrapType(edgeTypeInfo.type);
|
||||
const returnTypeInfo = types.find(
|
||||
(t: any) => t.name === returnType.name,
|
||||
);
|
||||
const returnNodeTypeInfo = returnTypeInfo?.fields?.find(
|
||||
(f: any) => f.name === 'node',
|
||||
);
|
||||
|
||||
if (returnNodeTypeInfo) {
|
||||
const nodeType = unwrapType(returnNodeTypeInfo.type);
|
||||
const nodeTypeInfo = types.find((t: any) => t.name === nodeType.name);
|
||||
|
||||
if (!nodeTypeInfo?.fields) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const content = generateTestContent(queryName, nodeTypeInfo?.fields);
|
||||
const result = writeTestFile(queryName, content, force);
|
||||
|
||||
if (result === 'created') createdCount++;
|
||||
if (result === 'updated') updatedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Number of tests created: ${createdCount}/${totalCount}`);
|
||||
if (force) {
|
||||
console.log(`Number of tests updated: ${updatedCount}/${totalCount}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Basic command-line argument parsing
|
||||
const forceArg = process.argv.includes('--force');
|
||||
|
||||
// Call the function with the parsed argument
|
||||
generateTests(forceArg);
|
||||
@@ -0,0 +1,89 @@
|
||||
export const INTROSPECTION_QUERY = `
|
||||
query IntrospectionQuery {
|
||||
__schema {
|
||||
queryType { name }
|
||||
mutationType { name }
|
||||
subscriptionType { name }
|
||||
types {
|
||||
...FullType
|
||||
}
|
||||
directives {
|
||||
name
|
||||
description
|
||||
locations
|
||||
args {
|
||||
...InputValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment FullType on __Type {
|
||||
kind
|
||||
name
|
||||
description
|
||||
fields(includeDeprecated: true) {
|
||||
name
|
||||
description
|
||||
args {
|
||||
...InputValue
|
||||
}
|
||||
type {
|
||||
...TypeRef
|
||||
}
|
||||
isDeprecated
|
||||
deprecationReason
|
||||
}
|
||||
inputFields {
|
||||
...InputValue
|
||||
}
|
||||
interfaces {
|
||||
...TypeRef
|
||||
}
|
||||
enumValues(includeDeprecated: true) {
|
||||
name
|
||||
description
|
||||
isDeprecated
|
||||
deprecationReason
|
||||
}
|
||||
possibleTypes {
|
||||
...TypeRef
|
||||
}
|
||||
}
|
||||
|
||||
fragment InputValue on __InputValue {
|
||||
name
|
||||
description
|
||||
type { ...TypeRef }
|
||||
defaultValue
|
||||
}
|
||||
|
||||
fragment TypeRef on __Type {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,60 @@
|
||||
export interface IntrospectionResponse {
|
||||
data: {
|
||||
__schema: Schema;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Schema {
|
||||
queryType: { name: string };
|
||||
mutationType: { name: string | null };
|
||||
subscriptionType: { name: string | null };
|
||||
types: GraphQLType[];
|
||||
directives: Directive[];
|
||||
}
|
||||
|
||||
export interface Directive {
|
||||
name: string;
|
||||
description: string | null;
|
||||
locations: string[];
|
||||
args: InputValue[];
|
||||
}
|
||||
|
||||
export interface GraphQLType {
|
||||
kind: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
fields?: Field[];
|
||||
inputFields?: InputValue[];
|
||||
interfaces?: TypeRef[];
|
||||
enumValues?: EnumValue[];
|
||||
possibleTypes?: TypeRef[];
|
||||
}
|
||||
|
||||
export interface Field {
|
||||
name: string;
|
||||
description: string | null;
|
||||
args: InputValue[];
|
||||
type: TypeRef;
|
||||
isDeprecated: boolean;
|
||||
deprecationReason: string | null;
|
||||
}
|
||||
|
||||
export interface InputValue {
|
||||
name: string;
|
||||
description: string | null;
|
||||
type: TypeRef;
|
||||
defaultValue: string | null;
|
||||
}
|
||||
|
||||
export interface TypeRef {
|
||||
kind: string;
|
||||
name: string | null;
|
||||
ofType: TypeRef | null;
|
||||
}
|
||||
|
||||
export interface EnumValue {
|
||||
name: string;
|
||||
description: string | null;
|
||||
isDeprecated: boolean;
|
||||
deprecationReason: string | null;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/run-integration.sh
|
||||
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
source $DIR/set-env-test.sh
|
||||
|
||||
npx nx database:reset
|
||||
npx nx jest --config ./test/jest-e2e.json
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/set-env-test.sh
|
||||
|
||||
# Get script's directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Construct the absolute path of .env file in the project root directory
|
||||
ENV_PATH="${SCRIPT_DIR}/../.env.test"
|
||||
|
||||
# Check if the file exists
|
||||
if [ -f "${ENV_PATH}" ]; then
|
||||
echo "🔵 - Loading environment variables from "${ENV_PATH}"..."
|
||||
# Export env vars
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
if echo "$line" | grep -F = &>/dev/null
|
||||
then
|
||||
varname=$(echo "$line" | cut -d '=' -f 1)
|
||||
varvalue=$(echo "$line" | cut -d '=' -f 2- | cut -d '#' -f 1)
|
||||
export "$varname"="$varvalue"
|
||||
fi
|
||||
done < <(grep -v '^#' "${ENV_PATH}")
|
||||
else
|
||||
echo "Error: ${ENV_PATH} does not exist."
|
||||
exit 1
|
||||
fi
|
||||
@@ -26,23 +26,15 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
||||
import { ModulesModule } from 'src/modules/modules.module';
|
||||
|
||||
import { CoreEngineModule } from './engine/core-modules/core-engine.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
// Nest.js devtools, use devtools.nestjs.com to debug
|
||||
// DevtoolsModule.registerAsync({
|
||||
// useFactory: (environmentService: EnvironmentService) => ({
|
||||
// http: environmentService.get('DEBUG_MODE'),
|
||||
// port: environmentService.get('DEBUG_PORT'),
|
||||
// }),
|
||||
// inject: [EnvironmentService],
|
||||
// }),
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
envFilePath: process.env.NODE_ENV === 'test' ? '.env.test' : '.env',
|
||||
}),
|
||||
GraphQLModule.forRootAsync<YogaDriverConfig>({
|
||||
driver: YogaDriver,
|
||||
imports: [CoreEngineModule, GraphQLConfigModule],
|
||||
imports: [GraphQLConfigModule],
|
||||
useClass: GraphQLConfigService,
|
||||
}),
|
||||
TwentyORMModule,
|
||||
|
||||
@@ -2,18 +2,24 @@ import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||
|
||||
import { config } from 'dotenv';
|
||||
import { DataSource, DataSourceOptions } from 'typeorm';
|
||||
config();
|
||||
config({ path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env' });
|
||||
|
||||
const isJest = process.argv.some((arg) => arg.includes('jest'));
|
||||
|
||||
export const typeORMCoreModuleOptions: TypeOrmModuleOptions = {
|
||||
url: process.env.PG_DATABASE_URL,
|
||||
type: 'postgres',
|
||||
logging: ['error'],
|
||||
schema: 'core',
|
||||
entities: ['dist/src/engine/core-modules/**/*.entity{.ts,.js}'],
|
||||
entities: [
|
||||
`${isJest ? '' : 'dist/'}src/engine/core-modules/**/*.entity{.ts,.js}`,
|
||||
],
|
||||
synchronize: false,
|
||||
migrationsRun: false,
|
||||
migrationsTableName: '_typeorm_migrations',
|
||||
migrations: ['dist/src/database/typeorm/core/migrations/*{.ts,.js}'],
|
||||
migrations: [
|
||||
`${isJest ? '' : 'dist/'}src/database/typeorm/core/migrations/*{.ts,.js}`,
|
||||
],
|
||||
ssl:
|
||||
process.env.PG_SSL_ALLOW_SELF_SIGNED === 'true'
|
||||
? {
|
||||
|
||||
@@ -2,18 +2,24 @@ import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||
|
||||
import { config } from 'dotenv';
|
||||
import { DataSource, DataSourceOptions } from 'typeorm';
|
||||
config();
|
||||
config({ path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env' });
|
||||
|
||||
const isJest = process.argv.some((arg) => arg.includes('jest'));
|
||||
|
||||
export const typeORMMetadataModuleOptions: TypeOrmModuleOptions = {
|
||||
url: process.env.PG_DATABASE_URL,
|
||||
type: 'postgres',
|
||||
logging: ['error'],
|
||||
schema: 'metadata',
|
||||
entities: ['dist/src/engine/metadata-modules/**/*.entity{.ts,.js}'],
|
||||
entities: [
|
||||
`${isJest ? '' : 'dist/'}src/engine/metadata-modules/**/*.entity{.ts,.js}`,
|
||||
],
|
||||
synchronize: false,
|
||||
migrationsRun: false,
|
||||
migrationsTableName: '_typeorm_migrations',
|
||||
migrations: ['dist/src/database/typeorm/metadata/migrations/*{.ts,.js}'],
|
||||
migrations: [
|
||||
`${isJest ? '' : 'dist/'}src/database/typeorm/metadata/migrations/*{.ts,.js}`,
|
||||
],
|
||||
ssl:
|
||||
process.env.PG_SSL_ALLOW_SELF_SIGNED === 'true'
|
||||
? {
|
||||
@@ -24,6 +30,7 @@ export const typeORMMetadataModuleOptions: TypeOrmModuleOptions = {
|
||||
query_timeout: 10000,
|
||||
},
|
||||
};
|
||||
|
||||
export const connectionSource = new DataSource(
|
||||
typeORMMetadataModuleOptions as DataSourceOptions,
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { config } from 'dotenv';
|
||||
import { DataSource, DataSourceOptions } from 'typeorm';
|
||||
config();
|
||||
config({ path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env' });
|
||||
|
||||
const typeORMRawModuleOptions: DataSourceOptions = {
|
||||
url: process.env.PG_DATABASE_URL,
|
||||
|
||||
@@ -4,15 +4,15 @@ import { DataSource } from 'typeorm';
|
||||
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
||||
import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity';
|
||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity';
|
||||
import { PostgresCredentials } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
|
||||
@Injectable()
|
||||
export class TypeORMService implements OnModuleInit, OnModuleDestroy {
|
||||
|
||||
@@ -5,6 +5,6 @@ import { CoreEngineModule } from 'src/engine/core-modules/core-engine.module';
|
||||
@Module({
|
||||
imports: [CoreEngineModule],
|
||||
providers: [],
|
||||
exports: [],
|
||||
exports: [CoreEngineModule],
|
||||
})
|
||||
export class GraphQLConfigModule {}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { ConfigurableModuleClass } from 'src/engine/core-modules/environment/environment.module-definition';
|
||||
import { validate } from 'src/engine/core-modules/environment/environment-variables';
|
||||
import { ConfigurableModuleClass } from 'src/engine/core-modules/environment/environment.module-definition';
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
@@ -12,6 +12,7 @@ import { validate } from 'src/engine/core-modules/environment/environment-variab
|
||||
isGlobal: true,
|
||||
expandVariables: true,
|
||||
validate,
|
||||
envFilePath: process.env.NODE_ENV === 'test' ? '.env.test' : '.env',
|
||||
}),
|
||||
],
|
||||
providers: [EnvironmentService],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export enum NodeEnvironment {
|
||||
test = 'test',
|
||||
development = 'development',
|
||||
production = 'production',
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.typ
|
||||
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
|
||||
import { handleExceptionAndConvertToGraphQLError } from 'src/engine/utils/global-exception-handler.util';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
class GraphqlTokenValidationProxy {
|
||||
private tokenService: TokenService;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { config } from 'dotenv';
|
||||
config();
|
||||
config({ path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env' });
|
||||
|
||||
export function generateFrontConfig(): void {
|
||||
const configObject = {
|
||||
|
||||
67
packages/twenty-server/test/activities.integration-spec.ts
Normal file
67
packages/twenty-server/test/activities.integration-spec.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('activitiesResolver (integration)', () => {
|
||||
it('should find many activities', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query activities {
|
||||
activities {
|
||||
edges {
|
||||
node {
|
||||
title
|
||||
body
|
||||
type
|
||||
reminderAt
|
||||
dueAt
|
||||
completedAt
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
authorId
|
||||
assigneeId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.activities;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const activities = edges[0].node;
|
||||
|
||||
expect(activities).toHaveProperty('title');
|
||||
expect(activities).toHaveProperty('body');
|
||||
expect(activities).toHaveProperty('type');
|
||||
expect(activities).toHaveProperty('reminderAt');
|
||||
expect(activities).toHaveProperty('dueAt');
|
||||
expect(activities).toHaveProperty('completedAt');
|
||||
expect(activities).toHaveProperty('id');
|
||||
expect(activities).toHaveProperty('createdAt');
|
||||
expect(activities).toHaveProperty('updatedAt');
|
||||
expect(activities).toHaveProperty('deletedAt');
|
||||
expect(activities).toHaveProperty('authorId');
|
||||
expect(activities).toHaveProperty('assigneeId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('activityTargetsResolver (integration)', () => {
|
||||
it('should find many activityTargets', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query activityTargets {
|
||||
activityTargets {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
activityId
|
||||
personId
|
||||
companyId
|
||||
opportunityId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.activityTargets;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const activityTargets = edges[0].node;
|
||||
|
||||
expect(activityTargets).toHaveProperty('id');
|
||||
expect(activityTargets).toHaveProperty('createdAt');
|
||||
expect(activityTargets).toHaveProperty('updatedAt');
|
||||
expect(activityTargets).toHaveProperty('deletedAt');
|
||||
expect(activityTargets).toHaveProperty('activityId');
|
||||
expect(activityTargets).toHaveProperty('personId');
|
||||
expect(activityTargets).toHaveProperty('companyId');
|
||||
expect(activityTargets).toHaveProperty('opportunityId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
57
packages/twenty-server/test/api-keys.integration-spec.ts
Normal file
57
packages/twenty-server/test/api-keys.integration-spec.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('apiKeysResolver (integration)', () => {
|
||||
it('should find many apiKeys', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query apiKeys {
|
||||
apiKeys {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
expiresAt
|
||||
revokedAt
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.apiKeys;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const apiKeys = edges[0].node;
|
||||
|
||||
expect(apiKeys).toHaveProperty('name');
|
||||
expect(apiKeys).toHaveProperty('expiresAt');
|
||||
expect(apiKeys).toHaveProperty('revokedAt');
|
||||
expect(apiKeys).toHaveProperty('id');
|
||||
expect(apiKeys).toHaveProperty('createdAt');
|
||||
expect(apiKeys).toHaveProperty('updatedAt');
|
||||
expect(apiKeys).toHaveProperty('deletedAt');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,31 +0,0 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
|
||||
import request from 'supertest';
|
||||
|
||||
import { createApp } from './utils/create-app';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
[app] = await createApp();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('/healthz (GET)', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/healthz')
|
||||
.expect(200)
|
||||
.expect((response) => {
|
||||
expect(response.body).toEqual({
|
||||
status: 'ok',
|
||||
info: { database: { status: 'up' } },
|
||||
error: {},
|
||||
details: { database: { status: 'up' } },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
71
packages/twenty-server/test/attachments.integration-spec.ts
Normal file
71
packages/twenty-server/test/attachments.integration-spec.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('attachmentsResolver (integration)', () => {
|
||||
it('should find many attachments', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query attachments {
|
||||
attachments {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
fullPath
|
||||
type
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
authorId
|
||||
activityId
|
||||
taskId
|
||||
noteId
|
||||
personId
|
||||
companyId
|
||||
opportunityId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.attachments;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const attachments = edges[0].node;
|
||||
|
||||
expect(attachments).toHaveProperty('name');
|
||||
expect(attachments).toHaveProperty('fullPath');
|
||||
expect(attachments).toHaveProperty('type');
|
||||
expect(attachments).toHaveProperty('id');
|
||||
expect(attachments).toHaveProperty('createdAt');
|
||||
expect(attachments).toHaveProperty('updatedAt');
|
||||
expect(attachments).toHaveProperty('deletedAt');
|
||||
expect(attachments).toHaveProperty('authorId');
|
||||
expect(attachments).toHaveProperty('activityId');
|
||||
expect(attachments).toHaveProperty('taskId');
|
||||
expect(attachments).toHaveProperty('noteId');
|
||||
expect(attachments).toHaveProperty('personId');
|
||||
expect(attachments).toHaveProperty('companyId');
|
||||
expect(attachments).toHaveProperty('opportunityId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
65
packages/twenty-server/test/audit-logs.integration-spec.ts
Normal file
65
packages/twenty-server/test/audit-logs.integration-spec.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('auditLogsResolver (integration)', () => {
|
||||
it('should find many auditLogs', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query auditLogs {
|
||||
auditLogs {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
properties
|
||||
context
|
||||
objectName
|
||||
objectMetadataId
|
||||
recordId
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
workspaceMemberId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.auditLogs;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const auditLogs = edges[0].node;
|
||||
|
||||
expect(auditLogs).toHaveProperty('name');
|
||||
expect(auditLogs).toHaveProperty('properties');
|
||||
expect(auditLogs).toHaveProperty('context');
|
||||
expect(auditLogs).toHaveProperty('objectName');
|
||||
expect(auditLogs).toHaveProperty('objectMetadataId');
|
||||
expect(auditLogs).toHaveProperty('recordId');
|
||||
expect(auditLogs).toHaveProperty('id');
|
||||
expect(auditLogs).toHaveProperty('createdAt');
|
||||
expect(auditLogs).toHaveProperty('updatedAt');
|
||||
expect(auditLogs).toHaveProperty('deletedAt');
|
||||
expect(auditLogs).toHaveProperty('workspaceMemberId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
80
packages/twenty-server/test/auth.integration-spec.ts
Normal file
80
packages/twenty-server/test/auth.integration-spec.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
const auth = {
|
||||
email: 'tim@apple.dev',
|
||||
password: 'Applecar2025',
|
||||
};
|
||||
|
||||
describe('AuthResolve (integration)', () => {
|
||||
let loginToken: string;
|
||||
|
||||
it('should challenge with email and password', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation Challenge {
|
||||
challenge(email: "${auth.email}", password: "${auth.password}") {
|
||||
loginToken {
|
||||
token
|
||||
expiresAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeDefined();
|
||||
expect(res.body.errors).toBeUndefined();
|
||||
})
|
||||
.expect((res) => {
|
||||
const data = res.body.data.challenge;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data.loginToken).toBeDefined();
|
||||
|
||||
loginToken = data.loginToken.token;
|
||||
});
|
||||
});
|
||||
|
||||
it('should verify with login token', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation Verify {
|
||||
verify(loginToken: "${loginToken}") {
|
||||
tokens {
|
||||
accessToken {
|
||||
token
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.data).toBeDefined();
|
||||
expect(res.body.errors).toBeUndefined();
|
||||
})
|
||||
.expect((res) => {
|
||||
const data = res.body.data.verify;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data.tokens).toBeDefined();
|
||||
|
||||
const accessToken = data.tokens.accessToken;
|
||||
|
||||
expect(accessToken).toBeDefined();
|
||||
expect(accessToken.token).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
55
packages/twenty-server/test/blocklists.integration-spec.ts
Normal file
55
packages/twenty-server/test/blocklists.integration-spec.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('blocklistsResolver (integration)', () => {
|
||||
it('should find many blocklists', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query blocklists {
|
||||
blocklists {
|
||||
edges {
|
||||
node {
|
||||
handle
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
workspaceMemberId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.blocklists;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const blocklists = edges[0].node;
|
||||
|
||||
expect(blocklists).toHaveProperty('handle');
|
||||
expect(blocklists).toHaveProperty('id');
|
||||
expect(blocklists).toHaveProperty('createdAt');
|
||||
expect(blocklists).toHaveProperty('updatedAt');
|
||||
expect(blocklists).toHaveProperty('deletedAt');
|
||||
expect(blocklists).toHaveProperty('workspaceMemberId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('calendarChannelEventAssociationsResolver (integration)', () => {
|
||||
it('should find many calendarChannelEventAssociations', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query calendarChannelEventAssociations {
|
||||
calendarChannelEventAssociations {
|
||||
edges {
|
||||
node {
|
||||
eventExternalId
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
calendarChannelId
|
||||
calendarEventId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.calendarChannelEventAssociations;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const calendarChannelEventAssociations = edges[0].node;
|
||||
|
||||
expect(calendarChannelEventAssociations).toHaveProperty(
|
||||
'eventExternalId',
|
||||
);
|
||||
expect(calendarChannelEventAssociations).toHaveProperty('id');
|
||||
expect(calendarChannelEventAssociations).toHaveProperty('createdAt');
|
||||
expect(calendarChannelEventAssociations).toHaveProperty('updatedAt');
|
||||
expect(calendarChannelEventAssociations).toHaveProperty('deletedAt');
|
||||
expect(calendarChannelEventAssociations).toHaveProperty(
|
||||
'calendarChannelId',
|
||||
);
|
||||
expect(calendarChannelEventAssociations).toHaveProperty(
|
||||
'calendarEventId',
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('calendarChannelsResolver (integration)', () => {
|
||||
it('should find many calendarChannels', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query calendarChannels {
|
||||
calendarChannels {
|
||||
edges {
|
||||
node {
|
||||
handle
|
||||
syncStatus
|
||||
syncStage
|
||||
visibility
|
||||
isContactAutoCreationEnabled
|
||||
contactAutoCreationPolicy
|
||||
isSyncEnabled
|
||||
syncCursor
|
||||
syncStageStartedAt
|
||||
throttleFailureCount
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
connectedAccountId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.calendarChannels;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const calendarChannels = edges[0].node;
|
||||
|
||||
expect(calendarChannels).toHaveProperty('handle');
|
||||
expect(calendarChannels).toHaveProperty('syncStatus');
|
||||
expect(calendarChannels).toHaveProperty('syncStage');
|
||||
expect(calendarChannels).toHaveProperty('visibility');
|
||||
expect(calendarChannels).toHaveProperty(
|
||||
'isContactAutoCreationEnabled',
|
||||
);
|
||||
expect(calendarChannels).toHaveProperty('contactAutoCreationPolicy');
|
||||
expect(calendarChannels).toHaveProperty('isSyncEnabled');
|
||||
expect(calendarChannels).toHaveProperty('syncCursor');
|
||||
expect(calendarChannels).toHaveProperty('syncStageStartedAt');
|
||||
expect(calendarChannels).toHaveProperty('throttleFailureCount');
|
||||
expect(calendarChannels).toHaveProperty('id');
|
||||
expect(calendarChannels).toHaveProperty('createdAt');
|
||||
expect(calendarChannels).toHaveProperty('updatedAt');
|
||||
expect(calendarChannels).toHaveProperty('deletedAt');
|
||||
expect(calendarChannels).toHaveProperty('connectedAccountId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('calendarEventParticipantsResolver (integration)', () => {
|
||||
it('should find many calendarEventParticipants', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query calendarEventParticipants {
|
||||
calendarEventParticipants {
|
||||
edges {
|
||||
node {
|
||||
handle
|
||||
displayName
|
||||
isOrganizer
|
||||
responseStatus
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
calendarEventId
|
||||
personId
|
||||
workspaceMemberId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.calendarEventParticipants;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const calendarEventParticipants = edges[0].node;
|
||||
|
||||
expect(calendarEventParticipants).toHaveProperty('handle');
|
||||
expect(calendarEventParticipants).toHaveProperty('displayName');
|
||||
expect(calendarEventParticipants).toHaveProperty('isOrganizer');
|
||||
expect(calendarEventParticipants).toHaveProperty('responseStatus');
|
||||
expect(calendarEventParticipants).toHaveProperty('id');
|
||||
expect(calendarEventParticipants).toHaveProperty('createdAt');
|
||||
expect(calendarEventParticipants).toHaveProperty('updatedAt');
|
||||
expect(calendarEventParticipants).toHaveProperty('deletedAt');
|
||||
expect(calendarEventParticipants).toHaveProperty('calendarEventId');
|
||||
expect(calendarEventParticipants).toHaveProperty('personId');
|
||||
expect(calendarEventParticipants).toHaveProperty('workspaceMemberId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
57
packages/twenty-server/test/comments.integration-spec.ts
Normal file
57
packages/twenty-server/test/comments.integration-spec.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('commentsResolver (integration)', () => {
|
||||
it('should find many comments', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query comments {
|
||||
comments {
|
||||
edges {
|
||||
node {
|
||||
body
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
authorId
|
||||
activityId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.comments;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const comments = edges[0].node;
|
||||
|
||||
expect(comments).toHaveProperty('body');
|
||||
expect(comments).toHaveProperty('id');
|
||||
expect(comments).toHaveProperty('createdAt');
|
||||
expect(comments).toHaveProperty('updatedAt');
|
||||
expect(comments).toHaveProperty('deletedAt');
|
||||
expect(comments).toHaveProperty('authorId');
|
||||
expect(comments).toHaveProperty('activityId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
67
packages/twenty-server/test/companies.integration-spec.ts
Normal file
67
packages/twenty-server/test/companies.integration-spec.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('companiesResolver (integration)', () => {
|
||||
it('should find many companies', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query companies {
|
||||
companies {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
employees
|
||||
idealCustomerProfile
|
||||
position
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
accountOwnerId
|
||||
tagline
|
||||
workPolicy
|
||||
visaSponsorship
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.companies;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const companies = edges[0].node;
|
||||
|
||||
expect(companies).toHaveProperty('name');
|
||||
expect(companies).toHaveProperty('employees');
|
||||
expect(companies).toHaveProperty('idealCustomerProfile');
|
||||
expect(companies).toHaveProperty('position');
|
||||
expect(companies).toHaveProperty('id');
|
||||
expect(companies).toHaveProperty('createdAt');
|
||||
expect(companies).toHaveProperty('updatedAt');
|
||||
expect(companies).toHaveProperty('deletedAt');
|
||||
expect(companies).toHaveProperty('accountOwnerId');
|
||||
expect(companies).toHaveProperty('tagline');
|
||||
expect(companies).toHaveProperty('workPolicy');
|
||||
expect(companies).toHaveProperty('visaSponsorship');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,310 +0,0 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
|
||||
import request from 'supertest';
|
||||
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
|
||||
import { createApp } from './utils/create-app';
|
||||
|
||||
describe('CompanyResolver (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
let companyId: string | undefined;
|
||||
|
||||
const authGuardMock = { canActivate: (): any => true };
|
||||
|
||||
beforeEach(async () => {
|
||||
[app] = await createApp({
|
||||
moduleBuilderHook: (moduleBuilder) =>
|
||||
moduleBuilder.overrideGuard(WorkspaceAuthGuard).useValue(authGuardMock),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should create a company', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation CreateOneCompany($data: CompanyCreateInput!) {
|
||||
createOneCompany(data: $data) {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
address {
|
||||
addressCity
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
data: {
|
||||
name: 'New Company',
|
||||
domainName: 'new-company.com',
|
||||
address: { addressCity: 'Paris' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post('/graphql')
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
const data = res.body.data.createOneCompany;
|
||||
|
||||
companyId = data.id;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data).toHaveProperty('id');
|
||||
expect(data).toHaveProperty('name', 'New Company');
|
||||
expect(data).toHaveProperty('domainName', 'new-company.com');
|
||||
expect(data).toHaveProperty('address', { addressCity: 'Paris' });
|
||||
});
|
||||
});
|
||||
|
||||
it('should find many companies', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query FindManyCompany {
|
||||
findManyCompany {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
address {
|
||||
addressCity
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post('/graphql')
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
const data = res.body.data.findManyCompany;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data)).toBe(true);
|
||||
expect(data.length).toBeGreaterThan(0);
|
||||
|
||||
const company = data.find((c) => c.id === companyId);
|
||||
|
||||
expect(company).toBeDefined();
|
||||
expect(company).toHaveProperty('id');
|
||||
expect(company).toHaveProperty('name', 'New Company');
|
||||
expect(company).toHaveProperty('domainName', 'new-company.com');
|
||||
expect(company).toHaveProperty('address', { addressCity: 'Paris' });
|
||||
|
||||
// Check if we have access to ressources outside of our workspace
|
||||
const instagramCompany = data.find((c) => c.name === 'Instagram');
|
||||
|
||||
expect(instagramCompany).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should find unique company', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query FindUniqueCompany($where: CompanyWhereUniqueInput!) {
|
||||
findUniqueCompany(where: $where) {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
address {
|
||||
addressCity
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
where: {
|
||||
id: companyId,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post('/graphql')
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
const data = res.body.data.findUniqueCompany;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data).toHaveProperty('id');
|
||||
expect(data).toHaveProperty('name', 'New Company');
|
||||
expect(data).toHaveProperty('domainName', 'new-company.com');
|
||||
expect(data).toHaveProperty('address', { addressCity: 'Paris' });
|
||||
});
|
||||
});
|
||||
|
||||
it('should not find unique company (forbidden because outside workspace)', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query FindUniqueCompany($where: CompanyWhereUniqueInput!) {
|
||||
findUniqueCompany(where: $where) {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
address {
|
||||
addressCity
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
where: {
|
||||
id: 'twenty-dev-a674fa6c-1455-4c57-afaf-dd5dc086361e',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post('/graphql')
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
const errors = res.body.errors;
|
||||
const error = errors?.[0];
|
||||
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe('Forbidden resource');
|
||||
});
|
||||
});
|
||||
|
||||
it('should update a company', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation UpdateOneCompany($where: CompanyWhereUniqueInput!, $data: CompanyUpdateInput!) {
|
||||
updateOneCompany(data: $data, where: $where) {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
address {
|
||||
addressCity
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
where: {
|
||||
id: companyId,
|
||||
},
|
||||
data: {
|
||||
name: 'Updated Company',
|
||||
domainName: 'updated-company.com',
|
||||
address: { addressCity: 'Updated City' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post('/graphql')
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
const data = res.body.data.updateOneCompany;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data).toHaveProperty('id');
|
||||
expect(data).toHaveProperty('name', 'Updated Company');
|
||||
expect(data).toHaveProperty('domainName', 'updated-company.com');
|
||||
expect(data).toHaveProperty('address', { addressCity: 'Updated City' });
|
||||
});
|
||||
});
|
||||
|
||||
it('should not update a company (forbidden because outside workspace)', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation UpdateOneCompany($where: CompanyWhereUniqueInput!, $data: CompanyUpdateInput!) {
|
||||
updateOneCompany(data: $data, where: $where) {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
address {
|
||||
addressCity
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
where: {
|
||||
id: 'twenty-dev-a674fa6c-1455-4c57-afaf-dd5dc086361e',
|
||||
},
|
||||
data: {
|
||||
name: 'Updated Instagram',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post('/graphql')
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
const errors = res.body.errors;
|
||||
const error = errors?.[0];
|
||||
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe('Forbidden resource');
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete a company', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation DeleteManyCompany($ids: [String!]) {
|
||||
deleteManyCompany(where: {id: {in: $ids}}) {
|
||||
count
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
ids: [companyId],
|
||||
},
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post('/graphql')
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
const data = res.body.data.deleteManyCompany;
|
||||
|
||||
companyId = undefined;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(data).toHaveProperty('count', 1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not delete a company (forbidden because outside workspace)', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
mutation DeleteManyCompany($ids: [String!]) {
|
||||
deleteManyCompany(where: {id: {in: $ids}}) {
|
||||
count
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
ids: ['twenty-dev-a674fa6c-1455-4c57-afaf-dd5dc086361e'],
|
||||
},
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post('/graphql')
|
||||
.send(queryData)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
const errors = res.body.errors;
|
||||
const error = errors?.[0];
|
||||
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe('Forbidden resource');
|
||||
});
|
||||
});
|
||||
});
|
||||
48
packages/twenty-server/test/company.integration-spec.ts
Normal file
48
packages/twenty-server/test/company.integration-spec.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const graphqlClient = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('CompanyResolver (integration)', () => {
|
||||
it('should find many companies', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query Companies {
|
||||
companies {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return graphqlClient
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.companies;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const company = edges[0].node;
|
||||
|
||||
expect(company).toBeDefined();
|
||||
expect(company).toHaveProperty('id');
|
||||
expect(company).toHaveProperty('name');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('connectedAccountsResolver (integration)', () => {
|
||||
it('should find many connectedAccounts', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query connectedAccounts {
|
||||
connectedAccounts {
|
||||
edges {
|
||||
node {
|
||||
handle
|
||||
provider
|
||||
accessToken
|
||||
refreshToken
|
||||
lastSyncHistoryId
|
||||
authFailedAt
|
||||
handleAliases
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
accountOwnerId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.connectedAccounts;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const connectedAccounts = edges[0].node;
|
||||
|
||||
expect(connectedAccounts).toHaveProperty('handle');
|
||||
expect(connectedAccounts).toHaveProperty('provider');
|
||||
expect(connectedAccounts).toHaveProperty('accessToken');
|
||||
expect(connectedAccounts).toHaveProperty('refreshToken');
|
||||
expect(connectedAccounts).toHaveProperty('lastSyncHistoryId');
|
||||
expect(connectedAccounts).toHaveProperty('authFailedAt');
|
||||
expect(connectedAccounts).toHaveProperty('handleAliases');
|
||||
expect(connectedAccounts).toHaveProperty('id');
|
||||
expect(connectedAccounts).toHaveProperty('createdAt');
|
||||
expect(connectedAccounts).toHaveProperty('updatedAt');
|
||||
expect(connectedAccounts).toHaveProperty('deletedAt');
|
||||
expect(connectedAccounts).toHaveProperty('accountOwnerId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
67
packages/twenty-server/test/favorites.integration-spec.ts
Normal file
67
packages/twenty-server/test/favorites.integration-spec.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('favoritesResolver (integration)', () => {
|
||||
it('should find many favorites', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query favorites {
|
||||
favorites {
|
||||
edges {
|
||||
node {
|
||||
position
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
workspaceMemberId
|
||||
personId
|
||||
companyId
|
||||
opportunityId
|
||||
taskId
|
||||
noteId
|
||||
viewId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.favorites;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const favorites = edges[0].node;
|
||||
|
||||
expect(favorites).toHaveProperty('position');
|
||||
expect(favorites).toHaveProperty('id');
|
||||
expect(favorites).toHaveProperty('createdAt');
|
||||
expect(favorites).toHaveProperty('updatedAt');
|
||||
expect(favorites).toHaveProperty('deletedAt');
|
||||
expect(favorites).toHaveProperty('workspaceMemberId');
|
||||
expect(favorites).toHaveProperty('personId');
|
||||
expect(favorites).toHaveProperty('companyId');
|
||||
expect(favorites).toHaveProperty('opportunityId');
|
||||
expect(favorites).toHaveProperty('taskId');
|
||||
expect(favorites).toHaveProperty('noteId');
|
||||
expect(favorites).toHaveProperty('viewId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"moduleFileExtensions": ["js", "json", "ts"],
|
||||
"rootDir": ".",
|
||||
"testEnvironment": "node",
|
||||
"testRegex": ".e2e-spec.ts$",
|
||||
"setupFilesAfterEnv": ["<rootDir>/utils/setup-tests.ts"],
|
||||
"moduleNameMapper": {
|
||||
"^src/(.*)": "<rootDir>/../src/$1",
|
||||
"^test/(.*)": "<rootDir>/$1"
|
||||
},
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('messageChannelMessageAssociationsResolver (integration)', () => {
|
||||
it('should find many messageChannelMessageAssociations', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query messageChannelMessageAssociations {
|
||||
messageChannelMessageAssociations {
|
||||
edges {
|
||||
node {
|
||||
createdAt
|
||||
messageExternalId
|
||||
messageThreadExternalId
|
||||
direction
|
||||
id
|
||||
updatedAt
|
||||
deletedAt
|
||||
messageChannelId
|
||||
messageId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.messageChannelMessageAssociations;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const messageChannelMessageAssociations = edges[0].node;
|
||||
|
||||
expect(messageChannelMessageAssociations).toHaveProperty('createdAt');
|
||||
expect(messageChannelMessageAssociations).toHaveProperty(
|
||||
'messageExternalId',
|
||||
);
|
||||
expect(messageChannelMessageAssociations).toHaveProperty(
|
||||
'messageThreadExternalId',
|
||||
);
|
||||
expect(messageChannelMessageAssociations).toHaveProperty('direction');
|
||||
expect(messageChannelMessageAssociations).toHaveProperty('id');
|
||||
expect(messageChannelMessageAssociations).toHaveProperty('updatedAt');
|
||||
expect(messageChannelMessageAssociations).toHaveProperty('deletedAt');
|
||||
expect(messageChannelMessageAssociations).toHaveProperty(
|
||||
'messageChannelId',
|
||||
);
|
||||
expect(messageChannelMessageAssociations).toHaveProperty('messageId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('messageChannelsResolver (integration)', () => {
|
||||
it('should find many messageChannels', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query messageChannels {
|
||||
messageChannels {
|
||||
edges {
|
||||
node {
|
||||
visibility
|
||||
handle
|
||||
type
|
||||
isContactAutoCreationEnabled
|
||||
contactAutoCreationPolicy
|
||||
excludeNonProfessionalEmails
|
||||
excludeGroupEmails
|
||||
isSyncEnabled
|
||||
syncCursor
|
||||
syncedAt
|
||||
syncStatus
|
||||
syncStage
|
||||
syncStageStartedAt
|
||||
throttleFailureCount
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
connectedAccountId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.messageChannels;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const messageChannels = edges[0].node;
|
||||
|
||||
expect(messageChannels).toHaveProperty('visibility');
|
||||
expect(messageChannels).toHaveProperty('handle');
|
||||
expect(messageChannels).toHaveProperty('type');
|
||||
expect(messageChannels).toHaveProperty(
|
||||
'isContactAutoCreationEnabled',
|
||||
);
|
||||
expect(messageChannels).toHaveProperty('contactAutoCreationPolicy');
|
||||
expect(messageChannels).toHaveProperty(
|
||||
'excludeNonProfessionalEmails',
|
||||
);
|
||||
expect(messageChannels).toHaveProperty('excludeGroupEmails');
|
||||
expect(messageChannels).toHaveProperty('isSyncEnabled');
|
||||
expect(messageChannels).toHaveProperty('syncCursor');
|
||||
expect(messageChannels).toHaveProperty('syncedAt');
|
||||
expect(messageChannels).toHaveProperty('syncStatus');
|
||||
expect(messageChannels).toHaveProperty('syncStage');
|
||||
expect(messageChannels).toHaveProperty('syncStageStartedAt');
|
||||
expect(messageChannels).toHaveProperty('throttleFailureCount');
|
||||
expect(messageChannels).toHaveProperty('id');
|
||||
expect(messageChannels).toHaveProperty('createdAt');
|
||||
expect(messageChannels).toHaveProperty('updatedAt');
|
||||
expect(messageChannels).toHaveProperty('deletedAt');
|
||||
expect(messageChannels).toHaveProperty('connectedAccountId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('messageParticipantsResolver (integration)', () => {
|
||||
it('should find many messageParticipants', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query messageParticipants {
|
||||
messageParticipants {
|
||||
edges {
|
||||
node {
|
||||
role
|
||||
handle
|
||||
displayName
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
messageId
|
||||
personId
|
||||
workspaceMemberId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.messageParticipants;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const messageParticipants = edges[0].node;
|
||||
|
||||
expect(messageParticipants).toHaveProperty('role');
|
||||
expect(messageParticipants).toHaveProperty('handle');
|
||||
expect(messageParticipants).toHaveProperty('displayName');
|
||||
expect(messageParticipants).toHaveProperty('id');
|
||||
expect(messageParticipants).toHaveProperty('createdAt');
|
||||
expect(messageParticipants).toHaveProperty('updatedAt');
|
||||
expect(messageParticipants).toHaveProperty('deletedAt');
|
||||
expect(messageParticipants).toHaveProperty('messageId');
|
||||
expect(messageParticipants).toHaveProperty('personId');
|
||||
expect(messageParticipants).toHaveProperty('workspaceMemberId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('messageThreadsResolver (integration)', () => {
|
||||
it('should find many messageThreads', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query messageThreads {
|
||||
messageThreads {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.messageThreads;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const messageThreads = edges[0].node;
|
||||
|
||||
expect(messageThreads).toHaveProperty('id');
|
||||
expect(messageThreads).toHaveProperty('createdAt');
|
||||
expect(messageThreads).toHaveProperty('updatedAt');
|
||||
expect(messageThreads).toHaveProperty('deletedAt');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"id": "20202020-a838-4fa9-b59b-96409b9a1c30",
|
||||
"firstName": "Tim",
|
||||
"lastName": "Apple",
|
||||
"email": "tim@apple.dev",
|
||||
"locale": "en",
|
||||
"passwordHash": "$2b$10$66d.6DuQExxnrfI9rMqOg.U1XIYpagr6Lv05uoWLYbYmtK0HDIvS6",
|
||||
"avatarUrl": null
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
59
packages/twenty-server/test/note-targets.integration-spec.ts
Normal file
59
packages/twenty-server/test/note-targets.integration-spec.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('noteTargetsResolver (integration)', () => {
|
||||
it('should find many noteTargets', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query noteTargets {
|
||||
noteTargets {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
noteId
|
||||
personId
|
||||
companyId
|
||||
opportunityId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.noteTargets;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const noteTargets = edges[0].node;
|
||||
|
||||
expect(noteTargets).toHaveProperty('id');
|
||||
expect(noteTargets).toHaveProperty('createdAt');
|
||||
expect(noteTargets).toHaveProperty('updatedAt');
|
||||
expect(noteTargets).toHaveProperty('deletedAt');
|
||||
expect(noteTargets).toHaveProperty('noteId');
|
||||
expect(noteTargets).toHaveProperty('personId');
|
||||
expect(noteTargets).toHaveProperty('companyId');
|
||||
expect(noteTargets).toHaveProperty('opportunityId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
57
packages/twenty-server/test/notes.integration-spec.ts
Normal file
57
packages/twenty-server/test/notes.integration-spec.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('notesResolver (integration)', () => {
|
||||
it('should find many notes', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query notes {
|
||||
notes {
|
||||
edges {
|
||||
node {
|
||||
position
|
||||
title
|
||||
body
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.notes;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const notes = edges[0].node;
|
||||
|
||||
expect(notes).toHaveProperty('position');
|
||||
expect(notes).toHaveProperty('title');
|
||||
expect(notes).toHaveProperty('body');
|
||||
expect(notes).toHaveProperty('id');
|
||||
expect(notes).toHaveProperty('createdAt');
|
||||
expect(notes).toHaveProperty('updatedAt');
|
||||
expect(notes).toHaveProperty('deletedAt');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
75
packages/twenty-server/test/objects.integration-spec.ts
Normal file
75
packages/twenty-server/test/objects.integration-spec.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('objectsResolver (integration)', () => {
|
||||
it('should find many objects', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query objects {
|
||||
objects {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
labelSingular
|
||||
labelPlural
|
||||
description
|
||||
icon
|
||||
isCustom
|
||||
isRemote
|
||||
isActive
|
||||
isSystem
|
||||
createdAt
|
||||
updatedAt
|
||||
labelIdentifierFieldMetadataId
|
||||
imageIdentifierFieldMetadataId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.objects;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const objects = edges[0].node;
|
||||
|
||||
expect(objects).toHaveProperty('id');
|
||||
expect(objects).toHaveProperty('dataSourceId');
|
||||
expect(objects).toHaveProperty('nameSingular');
|
||||
expect(objects).toHaveProperty('namePlural');
|
||||
expect(objects).toHaveProperty('labelSingular');
|
||||
expect(objects).toHaveProperty('labelPlural');
|
||||
expect(objects).toHaveProperty('description');
|
||||
expect(objects).toHaveProperty('icon');
|
||||
expect(objects).toHaveProperty('isCustom');
|
||||
expect(objects).toHaveProperty('isRemote');
|
||||
expect(objects).toHaveProperty('isActive');
|
||||
expect(objects).toHaveProperty('isSystem');
|
||||
expect(objects).toHaveProperty('createdAt');
|
||||
expect(objects).toHaveProperty('updatedAt');
|
||||
expect(objects).toHaveProperty('labelIdentifierFieldMetadataId');
|
||||
expect(objects).toHaveProperty('imageIdentifierFieldMetadataId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('opportunitiesResolver (integration)', () => {
|
||||
it('should find many opportunities', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query opportunities {
|
||||
opportunities {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
closeDate
|
||||
stage
|
||||
position
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
pointOfContactId
|
||||
companyId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.opportunities;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const opportunities = edges[0].node;
|
||||
|
||||
expect(opportunities).toHaveProperty('name');
|
||||
expect(opportunities).toHaveProperty('closeDate');
|
||||
expect(opportunities).toHaveProperty('stage');
|
||||
expect(opportunities).toHaveProperty('position');
|
||||
expect(opportunities).toHaveProperty('id');
|
||||
expect(opportunities).toHaveProperty('createdAt');
|
||||
expect(opportunities).toHaveProperty('updatedAt');
|
||||
expect(opportunities).toHaveProperty('deletedAt');
|
||||
expect(opportunities).toHaveProperty('pointOfContactId');
|
||||
expect(opportunities).toHaveProperty('companyId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
71
packages/twenty-server/test/people.integration-spec.ts
Normal file
71
packages/twenty-server/test/people.integration-spec.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('peopleResolver (integration)', () => {
|
||||
it('should find many people', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query people {
|
||||
people {
|
||||
edges {
|
||||
node {
|
||||
jobTitle
|
||||
phone
|
||||
city
|
||||
avatarUrl
|
||||
position
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
companyId
|
||||
intro
|
||||
whatsapp
|
||||
workPrefereance
|
||||
performanceRating
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.people;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const people = edges[0].node;
|
||||
|
||||
expect(people).toHaveProperty('jobTitle');
|
||||
expect(people).toHaveProperty('phone');
|
||||
expect(people).toHaveProperty('city');
|
||||
expect(people).toHaveProperty('avatarUrl');
|
||||
expect(people).toHaveProperty('position');
|
||||
expect(people).toHaveProperty('id');
|
||||
expect(people).toHaveProperty('createdAt');
|
||||
expect(people).toHaveProperty('updatedAt');
|
||||
expect(people).toHaveProperty('deletedAt');
|
||||
expect(people).toHaveProperty('companyId');
|
||||
expect(people).toHaveProperty('intro');
|
||||
expect(people).toHaveProperty('whatsapp');
|
||||
expect(people).toHaveProperty('workPrefereance');
|
||||
expect(people).toHaveProperty('performanceRating');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('serverlessFunctionsResolver (integration)', () => {
|
||||
it('should find many serverlessFunctions', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query serverlessFunctions {
|
||||
serverlessFunctions {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
description
|
||||
sourceCodeHash
|
||||
runtime
|
||||
latestVersion
|
||||
syncStatus
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.serverlessFunctions;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const serverlessFunctions = edges[0].node;
|
||||
|
||||
expect(serverlessFunctions).toHaveProperty('id');
|
||||
expect(serverlessFunctions).toHaveProperty('name');
|
||||
expect(serverlessFunctions).toHaveProperty('description');
|
||||
expect(serverlessFunctions).toHaveProperty('sourceCodeHash');
|
||||
expect(serverlessFunctions).toHaveProperty('runtime');
|
||||
expect(serverlessFunctions).toHaveProperty('latestVersion');
|
||||
expect(serverlessFunctions).toHaveProperty('syncStatus');
|
||||
expect(serverlessFunctions).toHaveProperty('createdAt');
|
||||
expect(serverlessFunctions).toHaveProperty('updatedAt');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
59
packages/twenty-server/test/task-targets.integration-spec.ts
Normal file
59
packages/twenty-server/test/task-targets.integration-spec.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('taskTargetsResolver (integration)', () => {
|
||||
it('should find many taskTargets', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query taskTargets {
|
||||
taskTargets {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
taskId
|
||||
personId
|
||||
companyId
|
||||
opportunityId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.taskTargets;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const taskTargets = edges[0].node;
|
||||
|
||||
expect(taskTargets).toHaveProperty('id');
|
||||
expect(taskTargets).toHaveProperty('createdAt');
|
||||
expect(taskTargets).toHaveProperty('updatedAt');
|
||||
expect(taskTargets).toHaveProperty('deletedAt');
|
||||
expect(taskTargets).toHaveProperty('taskId');
|
||||
expect(taskTargets).toHaveProperty('personId');
|
||||
expect(taskTargets).toHaveProperty('companyId');
|
||||
expect(taskTargets).toHaveProperty('opportunityId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
63
packages/twenty-server/test/tasks.integration-spec.ts
Normal file
63
packages/twenty-server/test/tasks.integration-spec.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('tasksResolver (integration)', () => {
|
||||
it('should find many tasks', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query tasks {
|
||||
tasks {
|
||||
edges {
|
||||
node {
|
||||
position
|
||||
title
|
||||
body
|
||||
dueAt
|
||||
status
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
assigneeId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.tasks;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const tasks = edges[0].node;
|
||||
|
||||
expect(tasks).toHaveProperty('position');
|
||||
expect(tasks).toHaveProperty('title');
|
||||
expect(tasks).toHaveProperty('body');
|
||||
expect(tasks).toHaveProperty('dueAt');
|
||||
expect(tasks).toHaveProperty('status');
|
||||
expect(tasks).toHaveProperty('id');
|
||||
expect(tasks).toHaveProperty('createdAt');
|
||||
expect(tasks).toHaveProperty('updatedAt');
|
||||
expect(tasks).toHaveProperty('deletedAt');
|
||||
expect(tasks).toHaveProperty('assigneeId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('timelineActivitiesResolver (integration)', () => {
|
||||
it('should find many timelineActivities', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query timelineActivities {
|
||||
timelineActivities {
|
||||
edges {
|
||||
node {
|
||||
happensAt
|
||||
name
|
||||
properties
|
||||
linkedRecordCachedName
|
||||
linkedRecordId
|
||||
linkedObjectMetadataId
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
workspaceMemberId
|
||||
personId
|
||||
companyId
|
||||
opportunityId
|
||||
noteId
|
||||
taskId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.timelineActivities;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const timelineActivities = edges[0].node;
|
||||
|
||||
expect(timelineActivities).toHaveProperty('happensAt');
|
||||
expect(timelineActivities).toHaveProperty('name');
|
||||
expect(timelineActivities).toHaveProperty('properties');
|
||||
expect(timelineActivities).toHaveProperty('linkedRecordCachedName');
|
||||
expect(timelineActivities).toHaveProperty('linkedRecordId');
|
||||
expect(timelineActivities).toHaveProperty('linkedObjectMetadataId');
|
||||
expect(timelineActivities).toHaveProperty('id');
|
||||
expect(timelineActivities).toHaveProperty('createdAt');
|
||||
expect(timelineActivities).toHaveProperty('updatedAt');
|
||||
expect(timelineActivities).toHaveProperty('deletedAt');
|
||||
expect(timelineActivities).toHaveProperty('workspaceMemberId');
|
||||
expect(timelineActivities).toHaveProperty('personId');
|
||||
expect(timelineActivities).toHaveProperty('companyId');
|
||||
expect(timelineActivities).toHaveProperty('opportunityId');
|
||||
expect(timelineActivities).toHaveProperty('noteId');
|
||||
expect(timelineActivities).toHaveProperty('taskId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,6 @@
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { Test, TestingModule, TestingModuleBuilder } from '@nestjs/testing';
|
||||
|
||||
import mockUser from 'test/mock-data/user.json';
|
||||
import mockWorkspace from 'test/mock-data/workspace.json';
|
||||
import { RequestHandler } from 'express';
|
||||
|
||||
import { AppModule } from 'src/app.module';
|
||||
|
||||
interface TestingModuleCreatePreHook {
|
||||
@@ -19,14 +15,14 @@ export type TestingAppCreatePreHook = (
|
||||
) => Promise<void>;
|
||||
|
||||
/**
|
||||
* Sets basic e2e testing module of app
|
||||
* Sets basic integration testing module of app
|
||||
*/
|
||||
export const createApp = async (
|
||||
config: {
|
||||
moduleBuilderHook?: TestingModuleCreatePreHook;
|
||||
appInitHook?: TestingAppCreatePreHook;
|
||||
} = {},
|
||||
): Promise<[NestExpressApplication, TestingModule]> => {
|
||||
): Promise<NestExpressApplication> => {
|
||||
let moduleBuilder: TestingModuleBuilder = Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
});
|
||||
@@ -36,21 +32,14 @@ export const createApp = async (
|
||||
}
|
||||
|
||||
const moduleFixture: TestingModule = await moduleBuilder.compile();
|
||||
|
||||
const app = moduleFixture.createNestApplication<NestExpressApplication>();
|
||||
|
||||
if (config.appInitHook) {
|
||||
await config.appInitHook(app);
|
||||
}
|
||||
|
||||
const mockAuthHandler: RequestHandler = (req, _res, next) => {
|
||||
req.user = {
|
||||
user: mockUser,
|
||||
workspace: mockWorkspace,
|
||||
};
|
||||
next();
|
||||
};
|
||||
await app.init();
|
||||
|
||||
app.use(mockAuthHandler);
|
||||
|
||||
return [await app.init(), moduleFixture];
|
||||
return app;
|
||||
};
|
||||
|
||||
16
packages/twenty-server/test/utils/setup-test.ts
Normal file
16
packages/twenty-server/test/utils/setup-test.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'tsconfig-paths/register';
|
||||
import { JestConfigWithTsJest } from 'ts-jest';
|
||||
|
||||
import { createApp } from './create-app';
|
||||
|
||||
export default async (_, projectConfig: JestConfigWithTsJest) => {
|
||||
const app = await createApp({});
|
||||
|
||||
if (!projectConfig.globals) {
|
||||
throw new Error('No globals found in project config');
|
||||
}
|
||||
|
||||
await app.listen(projectConfig.globals.APP_PORT);
|
||||
|
||||
global.app = app;
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
global.beforeEach(() => {
|
||||
// resetDb();
|
||||
});
|
||||
5
packages/twenty-server/test/utils/teardown-test.ts
Normal file
5
packages/twenty-server/test/utils/teardown-test.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import 'tsconfig-paths/register';
|
||||
|
||||
export default async () => {
|
||||
global.app.close();
|
||||
};
|
||||
61
packages/twenty-server/test/view-fields.integration-spec.ts
Normal file
61
packages/twenty-server/test/view-fields.integration-spec.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('viewFieldsResolver (integration)', () => {
|
||||
it('should find many viewFields', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query viewFields {
|
||||
viewFields {
|
||||
edges {
|
||||
node {
|
||||
fieldMetadataId
|
||||
isVisible
|
||||
size
|
||||
position
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
viewId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.viewFields;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const viewFields = edges[0].node;
|
||||
|
||||
expect(viewFields).toHaveProperty('fieldMetadataId');
|
||||
expect(viewFields).toHaveProperty('isVisible');
|
||||
expect(viewFields).toHaveProperty('size');
|
||||
expect(viewFields).toHaveProperty('position');
|
||||
expect(viewFields).toHaveProperty('id');
|
||||
expect(viewFields).toHaveProperty('createdAt');
|
||||
expect(viewFields).toHaveProperty('updatedAt');
|
||||
expect(viewFields).toHaveProperty('deletedAt');
|
||||
expect(viewFields).toHaveProperty('viewId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
61
packages/twenty-server/test/view-filters.integration-spec.ts
Normal file
61
packages/twenty-server/test/view-filters.integration-spec.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('viewFiltersResolver (integration)', () => {
|
||||
it('should find many viewFilters', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query viewFilters {
|
||||
viewFilters {
|
||||
edges {
|
||||
node {
|
||||
fieldMetadataId
|
||||
operand
|
||||
value
|
||||
displayValue
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
viewId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.viewFilters;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const viewFilters = edges[0].node;
|
||||
|
||||
expect(viewFilters).toHaveProperty('fieldMetadataId');
|
||||
expect(viewFilters).toHaveProperty('operand');
|
||||
expect(viewFilters).toHaveProperty('value');
|
||||
expect(viewFilters).toHaveProperty('displayValue');
|
||||
expect(viewFilters).toHaveProperty('id');
|
||||
expect(viewFilters).toHaveProperty('createdAt');
|
||||
expect(viewFilters).toHaveProperty('updatedAt');
|
||||
expect(viewFilters).toHaveProperty('deletedAt');
|
||||
expect(viewFilters).toHaveProperty('viewId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
57
packages/twenty-server/test/view-sorts.integration-spec.ts
Normal file
57
packages/twenty-server/test/view-sorts.integration-spec.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('viewSortsResolver (integration)', () => {
|
||||
it('should find many viewSorts', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query viewSorts {
|
||||
viewSorts {
|
||||
edges {
|
||||
node {
|
||||
fieldMetadataId
|
||||
direction
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
viewId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.viewSorts;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const viewSorts = edges[0].node;
|
||||
|
||||
expect(viewSorts).toHaveProperty('fieldMetadataId');
|
||||
expect(viewSorts).toHaveProperty('direction');
|
||||
expect(viewSorts).toHaveProperty('id');
|
||||
expect(viewSorts).toHaveProperty('createdAt');
|
||||
expect(viewSorts).toHaveProperty('updatedAt');
|
||||
expect(viewSorts).toHaveProperty('deletedAt');
|
||||
expect(viewSorts).toHaveProperty('viewId');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
67
packages/twenty-server/test/views.integration-spec.ts
Normal file
67
packages/twenty-server/test/views.integration-spec.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('viewsResolver (integration)', () => {
|
||||
it('should find many views', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query views {
|
||||
views {
|
||||
edges {
|
||||
node {
|
||||
position
|
||||
name
|
||||
objectMetadataId
|
||||
type
|
||||
key
|
||||
icon
|
||||
kanbanFieldMetadataId
|
||||
isCompact
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.views;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const views = edges[0].node;
|
||||
|
||||
expect(views).toHaveProperty('position');
|
||||
expect(views).toHaveProperty('name');
|
||||
expect(views).toHaveProperty('objectMetadataId');
|
||||
expect(views).toHaveProperty('type');
|
||||
expect(views).toHaveProperty('key');
|
||||
expect(views).toHaveProperty('icon');
|
||||
expect(views).toHaveProperty('kanbanFieldMetadataId');
|
||||
expect(views).toHaveProperty('isCompact');
|
||||
expect(views).toHaveProperty('id');
|
||||
expect(views).toHaveProperty('createdAt');
|
||||
expect(views).toHaveProperty('updatedAt');
|
||||
expect(views).toHaveProperty('deletedAt');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
57
packages/twenty-server/test/webhooks.integration-spec.ts
Normal file
57
packages/twenty-server/test/webhooks.integration-spec.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('webhooksResolver (integration)', () => {
|
||||
it('should find many webhooks', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query webhooks {
|
||||
webhooks {
|
||||
edges {
|
||||
node {
|
||||
targetUrl
|
||||
operation
|
||||
description
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.webhooks;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const webhooks = edges[0].node;
|
||||
|
||||
expect(webhooks).toHaveProperty('targetUrl');
|
||||
expect(webhooks).toHaveProperty('operation');
|
||||
expect(webhooks).toHaveProperty('description');
|
||||
expect(webhooks).toHaveProperty('id');
|
||||
expect(webhooks).toHaveProperty('createdAt');
|
||||
expect(webhooks).toHaveProperty('updatedAt');
|
||||
expect(webhooks).toHaveProperty('deletedAt');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
import request from 'supertest';
|
||||
|
||||
const client = request(`http://localhost:${APP_PORT}`);
|
||||
|
||||
describe('workspaceMembersResolver (integration)', () => {
|
||||
it('should find many workspaceMembers', () => {
|
||||
const queryData = {
|
||||
query: `
|
||||
query workspaceMembers {
|
||||
workspaceMembers {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
colorScheme
|
||||
avatarUrl
|
||||
locale
|
||||
timeZone
|
||||
dateFormat
|
||||
timeFormat
|
||||
userEmail
|
||||
userId
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
return client
|
||||
.post('/graphql')
|
||||
.set('Authorization', `Bearer ${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.workspaceMembers;
|
||||
|
||||
expect(data).toBeDefined();
|
||||
expect(Array.isArray(data.edges)).toBe(true);
|
||||
|
||||
const edges = data.edges;
|
||||
|
||||
if (edges.length > 0) {
|
||||
const workspaceMembers = edges[0].node;
|
||||
|
||||
expect(workspaceMembers).toHaveProperty('id');
|
||||
expect(workspaceMembers).toHaveProperty('colorScheme');
|
||||
expect(workspaceMembers).toHaveProperty('avatarUrl');
|
||||
expect(workspaceMembers).toHaveProperty('locale');
|
||||
expect(workspaceMembers).toHaveProperty('timeZone');
|
||||
expect(workspaceMembers).toHaveProperty('dateFormat');
|
||||
expect(workspaceMembers).toHaveProperty('timeFormat');
|
||||
expect(workspaceMembers).toHaveProperty('userEmail');
|
||||
expect(workspaceMembers).toHaveProperty('userId');
|
||||
expect(workspaceMembers).toHaveProperty('createdAt');
|
||||
expect(workspaceMembers).toHaveProperty('updatedAt');
|
||||
expect(workspaceMembers).toHaveProperty('deletedAt');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -27,7 +27,7 @@
|
||||
"paths": {
|
||||
"src/*": ["packages/twenty-server/src/*"],
|
||||
"test/*": ["packages/twenty-server/test/*"],
|
||||
"twenty-emails": ["packages/twenty-emails/src/index.ts"]
|
||||
"twenty-emails": ["packages/twenty-emails/dist"]
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
|
||||
5
packages/twenty-server/tsconfig.scripts.json
Normal file
5
packages/twenty-server/tsconfig.scripts.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["scripts/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user