Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/plugins/apps/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ export const PLUGIN_NAME: PluginName = 'datadog-apps-plugin' as const;
export const APPS_API_PATH = 'api/unstable/app-builder-code/apps';
export const ARCHIVE_FILENAME = 'datadog-apps-assets.zip';
export const BACKEND_FILE_RE = /\.backend\.(ts|tsx|js|jsx)$/;

/**
* Query suffix local execution appends to its `loadModule()`/`ssrLoadModule()`
* call so the transform hook below can tell "give me the real function body to
* run in-process" apart from a normal frontend import of the same file (which
* needs the client-side RPC-proxy stub instead). Follows Vite's own `?raw`/
* `?url`-style query-suffix convention rather than branching on the generic
* `options.ssr` flag, which would also match unrelated future SSR-context
* loads of the same file.
*/
export const LOCAL_EXECUTION_LOAD_SUFFIX = '?dd-local-exec';
export const LOCAL_EXECUTION_LOAD_RE = /\.backend\.(ts|tsx|js|jsx)\?dd-local-exec$/;
export const BACKEND_CODE_EXTENSIONS = [
'.ts',
'.tsx',
Expand Down
130 changes: 130 additions & 0 deletions packages/plugins/apps/src/vite/dev-server.integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Unless explicitly stated otherwise all files in this repository are licensed under the MIT License.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2019-Present Datadog, Inc.

/**
* Real end-to-end coverage for the local-execution path: no mocked
* `viteBuild`/`loadModule`, no hand-written stand-in module. This spins up
* a real Vite dev server (`createServer`, middleware mode — no port bound)
* rooted at the same `apps_backend_project` fixture `backend/integration.test.ts`
* uses, and lets its real `ssrLoadModule` import a real `.backend.ts` file
* directly and execute it via the real `/__dd/executeAction` HTTP handler —
* exactly the resolution path `vite/index.ts`'s `configureServer` wires up
* in production, including resolving `@datadog/apps-backend` from the
* fixture's own project root rather than build-plugins' own dependency tree.
*
* Uses `@datadog/apps-backend` (the fixture already has it as a real,
* locally-resolvable dependency — see `packages/tests/src/_jest/fixtures/
* node_modules/@datadog/apps-backend`) rather than `@datadog/action-catalog`
* (no equivalent local fixture package exists yet for it).
* `local-execution.test.ts` already separately proves a raw
* `$.Actions.foo.bar(...)` call and an action-catalog typed-wrapper call —
* which reduce to the same injected `executeAction` under the hood — route
* correctly. Building a real local `@datadog/action-catalog` fixture package
* is a reasonable, cheap follow-up, not required for this coverage to be
* meaningful.
*/

import { createDevServerMiddleware } from '@dd/apps-plugin/vite/dev-server';
import { getMockLogger } from '@dd/tests/_jest/helpers/mocks';
import { EventEmitter } from 'events';
import type { IncomingMessage, ServerResponse } from 'http';
import path from 'path';
import { build, createServer, type ViteDevServer } from 'vite';

import { encodeQueryName } from '../backend/encodeQueryName';
import type { BackendFunction } from '../backend/types';

const FIXTURE_ROOT = path.resolve(
__dirname,
'../../../../tests/src/_jest/fixtures/apps_backend_project',
);

const getRuntimeUsersFunc: BackendFunction = {
relativePath: 'getRuntimeUsers',
name: 'getRuntimeUsers',
absolutePath: path.join(FIXTURE_ROOT, 'getRuntimeUsers.backend.ts'),
allowedConnectionIds: [],
};

function createMockRequest(url: string, body: Record<string, unknown>): IncomingMessage {
const req = new EventEmitter() as unknown as IncomingMessage;
req.method = 'POST';
req.url = url;
process.nextTick(() => {
(req as unknown as EventEmitter).emit('data', Buffer.from(JSON.stringify(body)));
(req as unknown as EventEmitter).emit('end');
});
return req;
}

function createMockResponse() {
let body = '';
let resolveDone: () => void;
const done = new Promise<void>((resolve) => {
resolveDone = resolve;
});
const res = {
statusCode: 200,
setHeader: jest.fn(),
end: jest.fn((data: string) => {
body = data || '';
resolveDone();
}),
getBody() {
return body;
},
done,
};
return res as typeof res & ServerResponse;
}

describe('Dev Server Middleware — real end-to-end local execution', () => {
let server: ViteDevServer;

beforeAll(async () => {
server = await createServer({
configFile: false,
root: FIXTURE_ROOT,
logLevel: 'silent',
server: { middlewareMode: true, hmr: false },
ssr: { noExternal: true },
});
});

afterAll(async () => {
await server.close();
});

test('Should import a real backend function directly via the real Vite dev server and execute it locally, with a real @datadog/apps-backend typed import resolving $.Source correctly', async () => {
const middleware = createDevServerMiddleware(
build,
server.ssrLoadModule.bind(server),
() => [getRuntimeUsersFunc],
{ site: 'datadoghq.com' },
undefined, // no auth configured — this function never calls $.Actions
FIXTURE_ROOT,
getMockLogger(),
);

const req = createMockRequest('/__dd/executeAction', {
functionName: encodeQueryName(getRuntimeUsersFunc),
args: ['e2e-test'],
});
const res = createMockResponse();

middleware(req, res, jest.fn());
await res.done;

expect(res.statusCode).toBe(200);
const body = JSON.parse(res.getBody());
expect(body.success).toBe(true);
expect(body.result).toEqual({
data: {
label: 'e2e-test',
executionUser: { id: 'local-dev', orgId: 'local-dev-org' },
initiatingUser: { id: 'local-dev', orgId: 'local-dev-org' },
},
});
}, 30000);
});
Loading
Loading