KS
Killer-Skills

syncable-entity-testing — Categories.community

v1.0.0
GitHub

About this Skill

Perfect for Python Analysis Agents needing advanced entity testing capabilities. Building a modern alternative to Salesforce, powered by the community.

twentyhq twentyhq
[0]
[0]
Updated: 3/4/2026

Quality Score

Top 5%
31
Excellent
Based on code quality & docs
Installation
SYS Universal Install (Auto-Detect)
Cursor IDE Windsurf IDE VS Code IDE
> npx killer-skills add twentyhq/twenty

Agent Capability Analysis

The syncable-entity-testing MCP Server by twentyhq is an open-source Categories.community integration for Claude and other AI agents, enabling seamless task automation and capability expansion.

Ideal Agent Persona

Perfect for Python Analysis Agents needing advanced entity testing capabilities.

Core Value

Empowers agents to generate comprehensive test suites covering all validation scenarios, input transpilation exceptions, and successful use cases using libraries like 'unittest' and protocols like 'HTTP'.

Capabilities Granted for syncable-entity-testing MCP Server

Automating integration testing for syncable entities
Generating test cases for failing scenarios
Debugging successful use cases

! Prerequisites & Limits

  • Requires active database connection
  • Python 3.10+ only
Project
SKILL.md
13.0 KB
.cursorrules
1.2 KB
package.json
240 B
Ready
UTF-8

# Tags

[No tags]
SKILL.md
Readonly

Syncable Entity: Integration Testing (Step 6/6 - MANDATORY)

Purpose: Create comprehensive test suite covering all validation scenarios, input transpilation exceptions, and successful use cases.

When to use: After completing Steps 1-5. Integration tests are REQUIRED for all syncable entities.


Quick Start

Tests must cover:

  1. Failing scenarios - All validator exceptions and input transpilation errors
  2. Successful scenarios - All CRUD operations and edge cases
  3. Test utilities - Reusable query factories and helper functions

Test pattern: Two-file pattern (query factory + wrapper) for each operation.


Step 1: Create Test Utilities

Pattern: Query Factory

File: test/integration/metadata/suites/my-entity/utils/create-my-entity-query-factory.util.ts

typescript
1import gql from 'graphql-tag'; 2import { type PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; 3import { type CreateMyEntityInput } from 'src/engine/metadata-modules/my-entity/dtos/create-my-entity.input'; 4 5export type CreateMyEntityFactoryInput = CreateMyEntityInput; 6 7const DEFAULT_MY_ENTITY_GQL_FIELDS = ` 8 id 9 name 10 label 11 description 12 isCustom 13 createdAt 14 updatedAt 15`; 16 17export const createMyEntityQueryFactory = ({ 18 input, 19 gqlFields = DEFAULT_MY_ENTITY_GQL_FIELDS, 20}: PerformMetadataQueryParams<CreateMyEntityFactoryInput>) => ({ 21 query: gql` 22 mutation CreateMyEntity($input: CreateMyEntityInput!) { 23 createMyEntity(input: $input) { 24 ${gqlFields} 25 } 26 } 27 `, 28 variables: { 29 input, 30 }, 31});

Pattern: Wrapper Utility

File: test/integration/metadata/suites/my-entity/utils/create-my-entity.util.ts

typescript
1import { 2 type CreateMyEntityFactoryInput, 3 createMyEntityQueryFactory, 4} from 'test/integration/metadata/suites/my-entity/utils/create-my-entity-query-factory.util'; 5import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; 6import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type'; 7import { type PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; 8import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util'; 9import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; 10import { type MyEntityDto } from 'src/engine/metadata-modules/my-entity/dtos/my-entity.dto'; 11 12export const createMyEntity = async ({ 13 input, 14 gqlFields, 15 expectToFail = false, 16 token, 17}: PerformMetadataQueryParams<CreateMyEntityFactoryInput>): CommonResponseBody<{ 18 createMyEntity: MyEntityDto; 19}> => { 20 const graphqlOperation = createMyEntityQueryFactory({ 21 input, 22 gqlFields, 23 }); 24 25 const response = await makeMetadataAPIRequest(graphqlOperation, token); 26 27 if (expectToFail === true) { 28 warnIfNoErrorButExpectedToFail({ 29 response, 30 errorMessage: 'My entity creation should have failed but did not', 31 }); 32 } 33 34 if (expectToFail === false) { 35 warnIfErrorButNotExpectedToFail({ 36 response, 37 errorMessage: 'My entity creation has failed but should not', 38 }); 39 } 40 41 return { data: response.body.data, errors: response.body.errors }; 42};

Required utilities (follow same pattern):

  • update-my-entity-query-factory.util.ts + update-my-entity.util.ts
  • delete-my-entity-query-factory.util.ts + delete-my-entity.util.ts

Step 2: Failing Creation Tests

File: test/integration/metadata/suites/my-entity/failing-my-entity-creation.integration-spec.ts

typescript
1import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; 2import { createMyEntity } from 'test/integration/metadata/suites/my-entity/utils/create-my-entity.util'; 3import { deleteMyEntity } from 'test/integration/metadata/suites/my-entity/utils/delete-my-entity.util'; 4import { 5 eachTestingContextFilter, 6 type EachTestingContext, 7} from 'twenty-shared/testing'; 8import { isDefined } from 'twenty-shared/utils'; 9import { type CreateMyEntityInput } from 'src/engine/metadata-modules/my-entity/dtos/create-my-entity.input'; 10 11type TestContext = { 12 input: CreateMyEntityInput; 13}; 14 15type GlobalTestContext = { 16 existingEntityLabel: string; 17 existingEntityName: string; 18}; 19 20const globalTestContext: GlobalTestContext = { 21 existingEntityLabel: 'Existing Test Entity', 22 existingEntityName: 'existingTestEntity', 23}; 24 25type CreateMyEntityTestingContext = EachTestingContext<TestContext>[]; 26 27describe('My entity creation should fail', () => { 28 let existingEntityId: string | undefined; 29 30 beforeAll(async () => { 31 // Setup: Create entity for uniqueness tests 32 const { data } = await createMyEntity({ 33 expectToFail: false, 34 input: { 35 name: globalTestContext.existingEntityName, 36 label: globalTestContext.existingEntityLabel, 37 }, 38 }); 39 40 existingEntityId = data.createMyEntity.id; 41 }); 42 43 afterAll(async () => { 44 // Cleanup 45 if (isDefined(existingEntityId)) { 46 await deleteMyEntity({ 47 expectToFail: false, 48 input: { id: existingEntityId }, 49 }); 50 } 51 }); 52 53 const failingMyEntityCreationTestCases: CreateMyEntityTestingContext = [ 54 // Input transpilation validation 55 { 56 title: 'when name is missing', 57 context: { 58 input: { 59 label: 'Entity Missing Name', 60 } as CreateMyEntityInput, 61 }, 62 }, 63 { 64 title: 'when label is missing', 65 context: { 66 input: { 67 name: 'entityMissingLabel', 68 } as CreateMyEntityInput, 69 }, 70 }, 71 { 72 title: 'when name is empty string', 73 context: { 74 input: { 75 name: '', 76 label: 'Empty Name Entity', 77 }, 78 }, 79 }, 80 81 // Validator business logic 82 { 83 title: 'when name already exists (uniqueness)', 84 context: { 85 input: { 86 name: globalTestContext.existingEntityName, 87 label: 'Duplicate Name Entity', 88 }, 89 }, 90 }, 91 { 92 title: 'when trying to create standard entity', 93 context: { 94 input: { 95 name: 'myEntity', 96 label: 'Standard Entity', 97 isCustom: false, 98 } as CreateMyEntityInput, 99 }, 100 }, 101 102 // Foreign key validation 103 { 104 title: 'when parentEntityId does not exist', 105 context: { 106 input: { 107 name: 'invalidParentEntity', 108 label: 'Invalid Parent Entity', 109 parentEntityId: '00000000-0000-0000-0000-000000000000', 110 }, 111 }, 112 }, 113 ]; 114 115 it.each(eachTestingContextFilter(failingMyEntityCreationTestCases))( 116 '$title', 117 async ({ context }) => { 118 const { errors } = await createMyEntity({ 119 expectToFail: true, 120 input: context.input, 121 }); 122 123 expectOneNotInternalServerErrorSnapshot({ 124 errors, 125 }); 126 }, 127 ); 128});

Test coverage requirements:

  • ✅ Missing required fields
  • ✅ Empty strings
  • ✅ Invalid format
  • ✅ Uniqueness violations
  • ✅ Standard entity protection
  • ✅ Foreign key validation

Step 3: Successful Creation Tests

File: test/integration/metadata/suites/my-entity/successful-my-entity-creation.integration-spec.ts

typescript
1import { createMyEntity } from 'test/integration/metadata/suites/my-entity/utils/create-my-entity.util'; 2import { deleteMyEntity } from 'test/integration/metadata/suites/my-entity/utils/delete-my-entity.util'; 3import { type CreateMyEntityInput } from 'src/engine/metadata-modules/my-entity/dtos/create-my-entity.input'; 4 5describe('My entity creation should succeed', () => { 6 let createdEntityId: string; 7 8 afterEach(async () => { 9 if (createdEntityId) { 10 await deleteMyEntity({ 11 expectToFail: false, 12 input: { id: createdEntityId }, 13 }); 14 } 15 }); 16 17 it('should create entity with minimal required input', async () => { 18 const { data } = await createMyEntity({ 19 expectToFail: false, 20 input: { 21 name: 'minimalEntity', 22 label: 'Minimal Entity', 23 }, 24 }); 25 26 createdEntityId = data?.createMyEntity?.id; 27 28 expect(data.createMyEntity).toMatchObject({ 29 id: expect.any(String), 30 name: 'minimalEntity', 31 label: 'Minimal Entity', 32 description: null, 33 isCustom: true, 34 createdAt: expect.any(String), 35 updatedAt: expect.any(String), 36 }); 37 }); 38 39 it('should create entity with all optional fields', async () => { 40 const input = { 41 name: 'fullEntity', 42 label: 'Full Entity', 43 description: 'Entity with all fields specified', 44 } as const satisfies CreateMyEntityInput; 45 46 const { data } = await createMyEntity({ 47 expectToFail: false, 48 input, 49 }); 50 51 createdEntityId = data?.createMyEntity?.id; 52 53 expect(data.createMyEntity).toMatchObject({ 54 id: expect.any(String), 55 name: 'fullEntity', 56 label: 'Full Entity', 57 description: 'Entity with all fields specified', 58 isCustom: true, 59 }); 60 }); 61 62 it('should sanitize input by trimming whitespace', async () => { 63 const { data } = await createMyEntity({ 64 expectToFail: false, 65 input: { 66 name: ' entityWithSpaces ', 67 label: ' Entity With Spaces ', 68 description: ' Description with spaces ', 69 }, 70 }); 71 72 createdEntityId = data?.createMyEntity?.id; 73 74 expect(data.createMyEntity).toMatchObject({ 75 id: expect.any(String), 76 name: 'entityWithSpaces', 77 label: 'Entity With Spaces', 78 description: 'Description with spaces', 79 }); 80 }); 81 82 it('should handle long text content', async () => { 83 const longDescription = 'A'.repeat(1000); 84 85 const { data } = await createMyEntity({ 86 expectToFail: false, 87 input: { 88 name: 'longDescEntity', 89 label: 'Long Description Entity', 90 description: longDescription, 91 }, 92 }); 93 94 createdEntityId = data?.createMyEntity?.id; 95 96 expect(data.createMyEntity).toMatchObject({ 97 id: expect.any(String), 98 description: longDescription, 99 }); 100 }); 101});

Test coverage requirements:

  • ✅ Minimal required input
  • ✅ All optional fields
  • ✅ Input sanitization
  • ✅ Long text content
  • ✅ Special characters

Step 4: Update and Delete Tests

Create similar test files for update and delete operations:

Required files:

  • failing-my-entity-update.integration-spec.ts
  • successful-my-entity-update.integration-spec.ts
  • failing-my-entity-deletion.integration-spec.ts
  • successful-my-entity-deletion.integration-spec.ts

Testing Best Practices

Pattern: Cleanup

typescript
1afterEach(async () => { 2 if (createdEntityId) { 3 await deleteMyEntity({ 4 expectToFail: false, 5 input: { id: createdEntityId }, 6 }); 7 } 8});

Pattern: Type-Safe Inputs

typescript
1const input = { 2 name: 'myEntity', 3 label: 'My Entity', 4} as const satisfies CreateMyEntityInput;

Pattern: Snapshot Testing

typescript
1expectOneNotInternalServerErrorSnapshot({ 2 errors, 3});

Running Tests

bash
1# Run all entity tests 2npx jest test/integration/metadata/suites/my-entity --config=packages/twenty-server/jest.config.mjs 3 4# Run specific test file 5npx jest test/integration/metadata/suites/my-entity/failing-my-entity-creation.integration-spec.ts --config=packages/twenty-server/jest.config.mjs 6 7# Update snapshots 8npx jest test/integration/metadata/suites/my-entity --updateSnapshot --config=packages/twenty-server/jest.config.mjs

Complete Test Checklist

Test Utilities

  • create-my-entity-query-factory.util.ts created
  • create-my-entity.util.ts created
  • update-my-entity-query-factory.util.ts created
  • update-my-entity.util.ts created
  • delete-my-entity-query-factory.util.ts created
  • delete-my-entity.util.ts created

Failing Tests Coverage

  • Missing required fields
  • Empty string validation
  • Uniqueness violations
  • Standard entity protection
  • Foreign key validation
  • JSONB property validation (if applicable)

Successful Tests Coverage

  • Create with minimal input
  • Create with all optional fields
  • Input sanitization (whitespace)
  • Long text content
  • Update single field
  • Update multiple fields
  • Successful deletion

Snapshot Tests

  • All failing tests use expectOneNotInternalServerErrorSnapshot
  • Snapshots committed to __snapshots__/ directory

Success Criteria

Your integration tests are complete when:

✅ All test utilities created (minimum 6 files) ✅ Failing creation tests cover all validators ✅ Failing update tests cover business rules ✅ Failing deletion tests cover protection rules ✅ Successful tests cover all use cases ✅ All snapshots generated and committed ✅ All tests pass consistently ✅ Test coverage meets requirements (>80%)


Final Step

Step 6 Complete! → Your syncable entity is fully tested and production-ready!

Congratulations! You've successfully created a new syncable entity in Twenty's workspace migration system.

For complete workflow, see @creating-syncable-entity rule.

Related Skills

Looking for an alternative to syncable-entity-testing or building a Categories.community AI Agent? Explore these related open-source MCP Servers.

View All

widget-generator

Logo of f
f

widget-generator is an open-source AI agent skill for creating widget plugins that are injected into prompt feeds on prompts.chat. It supports two rendering modes: standard prompt widgets using default PromptCard styling and custom render widgets built as full React components.

149.6k
0
Design

chat-sdk

Logo of lobehub
lobehub

chat-sdk is a unified TypeScript SDK for building chat bots across multiple platforms, providing a single interface for deploying bot logic.

73.0k
0
Communication

zustand

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication

data-fetching

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication