Skip to content

Commit ae53df1

Browse files
committed
feat(apps): wire local execution into the real dev server
Wires the new direct-import local-execution path (local-execution.ts) into the real Vite dev server: threads server.ssrLoadModule through as the loadModule dependency, drops the bundling step from /__dd/executeAction entirely (debugBundle and executeActionViaCloud still bundle, unchanged), and forwards connectionId end-to-end through makeExecuteActionRemotely so a $.Actions call naming a specific connection actually reaches it instead of being silently dropped. Also forces @datadog/apps-backend and @datadog/action-catalog through Vite's SSR transform pipeline (ssr.noExternal) rather than letting the dev server's default node_modules externalization `require()` them directly -- both ship ESM-only, so an externalized `require()` throws "Cannot use import statement outside a module".
1 parent 41a772e commit ae53df1

5 files changed

Lines changed: 584 additions & 51 deletions

File tree

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed under the MIT License.
2+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
3+
// Copyright 2019-Present Datadog, Inc.
4+
5+
/**
6+
* Real end-to-end coverage for the local-execution path: no mocked
7+
* `viteBuild`/`loadModule`, no hand-written stand-in module. This spins up
8+
* a real Vite dev server (`createServer`, middleware mode — no port bound)
9+
* rooted at the same `apps_backend_project` fixture `backend/integration.test.ts`
10+
* uses, and lets its real `ssrLoadModule` import a real `.backend.ts` file
11+
* directly and execute it via the real `/__dd/executeAction` HTTP handler —
12+
* exactly the resolution path `vite/index.ts`'s `configureServer` wires up
13+
* in production, including resolving `@datadog/apps-backend` from the
14+
* fixture's own project root rather than build-plugins' own dependency tree.
15+
*
16+
* Uses `@datadog/apps-backend` (the fixture already has it as a real,
17+
* locally-resolvable dependency — see `packages/tests/src/_jest/fixtures/
18+
* node_modules/@datadog/apps-backend`) rather than `@datadog/action-catalog`
19+
* (no equivalent local fixture package exists yet for it).
20+
* `local-execution.test.ts` already separately proves a raw
21+
* `$.Actions.foo.bar(...)` call and an action-catalog typed-wrapper call —
22+
* which reduce to the same injected `executeAction` under the hood — route
23+
* correctly. Building a real local `@datadog/action-catalog` fixture package
24+
* is a reasonable, cheap follow-up, not required for this coverage to be
25+
* meaningful.
26+
*/
27+
28+
import { createDevServerMiddleware } from '@dd/apps-plugin/vite/dev-server';
29+
import { getMockLogger } from '@dd/tests/_jest/helpers/mocks';
30+
import { EventEmitter } from 'events';
31+
import type { IncomingMessage, ServerResponse } from 'http';
32+
import path from 'path';
33+
import { build, createServer, type ViteDevServer } from 'vite';
34+
35+
import { encodeQueryName } from '../backend/encodeQueryName';
36+
import type { BackendFunction } from '../backend/types';
37+
38+
const FIXTURE_ROOT = path.resolve(
39+
__dirname,
40+
'../../../../tests/src/_jest/fixtures/apps_backend_project',
41+
);
42+
43+
const getRuntimeUsersFunc: BackendFunction = {
44+
relativePath: 'getRuntimeUsers',
45+
name: 'getRuntimeUsers',
46+
absolutePath: path.join(FIXTURE_ROOT, 'getRuntimeUsers.backend.ts'),
47+
allowedConnectionIds: [],
48+
};
49+
50+
function createMockRequest(url: string, body: Record<string, unknown>): IncomingMessage {
51+
const req = new EventEmitter() as unknown as IncomingMessage;
52+
req.method = 'POST';
53+
req.url = url;
54+
process.nextTick(() => {
55+
(req as unknown as EventEmitter).emit('data', Buffer.from(JSON.stringify(body)));
56+
(req as unknown as EventEmitter).emit('end');
57+
});
58+
return req;
59+
}
60+
61+
function createMockResponse() {
62+
let body = '';
63+
let resolveDone: () => void;
64+
const done = new Promise<void>((resolve) => {
65+
resolveDone = resolve;
66+
});
67+
const res = {
68+
statusCode: 200,
69+
setHeader: jest.fn(),
70+
end: jest.fn((data: string) => {
71+
body = data || '';
72+
resolveDone();
73+
}),
74+
getBody() {
75+
return body;
76+
},
77+
done,
78+
};
79+
return res as typeof res & ServerResponse;
80+
}
81+
82+
describe('Dev Server Middleware — real end-to-end local execution', () => {
83+
let server: ViteDevServer;
84+
85+
beforeAll(async () => {
86+
server = await createServer({
87+
configFile: false,
88+
root: FIXTURE_ROOT,
89+
logLevel: 'silent',
90+
server: { middlewareMode: true, hmr: false },
91+
ssr: { noExternal: true },
92+
});
93+
});
94+
95+
afterAll(async () => {
96+
await server.close();
97+
});
98+
99+
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 () => {
100+
const middleware = createDevServerMiddleware(
101+
build,
102+
server.ssrLoadModule.bind(server),
103+
() => [getRuntimeUsersFunc],
104+
{ site: 'datadoghq.com' },
105+
undefined, // no auth configured — this function never calls $.Actions
106+
FIXTURE_ROOT,
107+
getMockLogger(),
108+
);
109+
110+
const req = createMockRequest('/__dd/executeAction', {
111+
functionName: encodeQueryName(getRuntimeUsersFunc),
112+
args: ['e2e-test'],
113+
});
114+
const res = createMockResponse();
115+
116+
middleware(req, res, jest.fn());
117+
await res.done;
118+
119+
expect(res.statusCode).toBe(200);
120+
const body = JSON.parse(res.getBody());
121+
expect(body.success).toBe(true);
122+
expect(body.result).toEqual({
123+
data: {
124+
label: 'e2e-test',
125+
executionUser: { id: 'local-dev', orgId: 'local-dev-org' },
126+
initiatingUser: { id: 'local-dev', orgId: 'local-dev-org' },
127+
},
128+
});
129+
}, 30000);
130+
});

0 commit comments

Comments
 (0)