Add tests for CoreApiClient stub issue (PR #18460) with clean tsconfig fix

Exclude test app src/ directories from SDK tsconfig.json instead of
using a .d.ts hack. These directories contain standalone app code
(with their own tsconfig.json) that should not be typechecked as SDK
root files.

- Exclude src/app-seeds/*/src/** and src/cli/__tests__/apps/*/src/**
- Add check-core-client logic function that verifies CoreApiClient
- Add E2E test asserting CoreApiClient has real query/mutation methods
- Add app-build integration test documenting current stub behavior

Made-with: Cursor
This commit is contained in:
Félix Malfait
2026-03-10 09:28:19 +01:00
parent fc84ff040d
commit 6d266e3e7b
4 changed files with 116 additions and 1 deletions
@@ -0,0 +1,67 @@
import { readdir, rm } from 'node:fs/promises';
import { join } from 'path';
import { appBuild } from '@/cli/public-operations/app-build';
import { pathExists } from '@/cli/utilities/file/fs-utils';
import { GENERATED_DIR, OUTPUT_DIR } from 'twenty-shared/application';
const APP_PATH = join(__dirname, '../..');
describe('rich-app app:build', () => {
let buildResult: Awaited<ReturnType<typeof appBuild>>;
beforeAll(async () => {
const generatedPath = join(
APP_PATH,
'node_modules',
'twenty-sdk',
GENERATED_DIR,
);
await rm(generatedPath, { recursive: true, force: true });
buildResult = await appBuild({ appPath: APP_PATH });
}, 60_000);
it('should complete build successfully', () => {
expect(buildResult.success).toBe(true);
});
it('should produce manifest in output directory', async () => {
const manifestPath = join(APP_PATH, OUTPUT_DIR, 'manifest.json');
expect(await pathExists(manifestPath)).toBe(true);
});
it('should produce built logic function files', async () => {
const outputDir = join(APP_PATH, OUTPUT_DIR);
const files = await readdir(outputDir, { recursive: true });
const functionFiles = files
.map((file) => file.toString())
.filter((file) => file.endsWith('.function.mjs'))
.sort();
expect(functionFiles.length).toBeGreaterThan(0);
});
// app:build only creates client stubs (empty CoreApiClient / MetadataApiClient)
// because it doesn't sync with the server or generate the real API client.
// Logic functions that use CoreApiClient will get an empty class at runtime.
// When this is fixed, update this test to assert the client IS fully generated.
// See: https://github.com/twentyhq/twenty/pull/18460
it('should only produce client stubs (not a fully generated CoreApiClient)', async () => {
const clientIndexPath = join(
APP_PATH,
'node_modules',
'twenty-sdk',
GENERATED_DIR,
'core',
'index.ts',
);
const { readFile } = await import('node:fs/promises');
const content = await readFile(clientIndexPath, 'utf-8');
expect(content).toBe('export class CoreApiClient {}\n');
});
});
@@ -76,4 +76,24 @@ describe('functionExecute E2E', () => {
error: { code: 'FUNCTION_NOT_FOUND' },
});
});
// Verifies that CoreApiClient is fully generated (not just stubs) after
// appGenerateClient. If app:build is used without client generation, the
// bundled CoreApiClient would be an empty stub and this test would fail.
// See: https://github.com/twentyhq/twenty/pull/18460
it('should execute a function that uses CoreApiClient with real query/mutation methods', async () => {
const result = await functionExecute({
appPath: APP_PATH,
functionName: 'check-core-client',
});
expect(result).toMatchObject({
success: true,
data: {
functionName: 'check-core-client',
status: 'SUCCESS',
data: { hasQuery: true, hasMutation: true },
},
});
});
});
@@ -0,0 +1,26 @@
import { CoreApiClient } from 'twenty-sdk/generated';
import { defineLogicFunction } from '@/sdk';
export const CHECK_CORE_CLIENT_UNIVERSAL_IDENTIFIER =
'b7c8d9e0-1a2b-4c3d-8e5f-6a7b8c9d0e1f';
const checkCoreClientHandler = () => {
const client = new CoreApiClient();
return {
hasQuery: typeof client.query === 'function',
hasMutation: typeof client.mutation === 'function',
};
};
export default defineLogicFunction({
universalIdentifier: CHECK_CORE_CLIENT_UNIVERSAL_IDENTIFIER,
name: 'check-core-client',
timeoutSeconds: 5,
handler: checkCoreClientHandler,
httpRouteTriggerSettings: {
path: '/check-core-client',
httpMethod: 'GET',
isAuthRequired: false,
},
});
+3 -1
View File
@@ -36,6 +36,8 @@
"src/front-component-renderer/host/generated/host-component-registry.ts",
"src/front-component-renderer/remote/generated/remote-components.ts",
"src/front-component-renderer/remote/generated/remote-elements.ts",
"src/front-component-renderer/__stories__/**/*"
"src/front-component-renderer/__stories__/**/*",
"src/app-seeds/*/src/**/*",
"src/cli/__tests__/apps/*/src/**/*"
]
}