@flink-app/flink 1.0.0 → 2.0.0-alpha.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +991 -0
- package/SCHEMA_EXTRACTION_ANALYSIS.md +494 -0
- package/SIMPLE_AST_FEASIBILITY.md +570 -0
- package/bin/flink.ts +13 -2
- package/cli/build.ts +24 -44
- package/cli/clean.ts +13 -25
- package/cli/cli-utils.ts +190 -17
- package/cli/dev.ts +252 -0
- package/cli/loadEnvFiles.ts +116 -0
- package/cli/run.ts +45 -62
- package/dist/bin/flink.js +61 -2
- package/dist/cli/build.js +20 -25
- package/dist/cli/clean.js +12 -10
- package/dist/cli/cli-utils.d.ts +34 -3
- package/dist/cli/cli-utils.js +193 -12
- package/dist/cli/dev.d.ts +2 -0
- package/dist/cli/dev.js +279 -0
- package/dist/cli/loadEnvFiles.d.ts +30 -0
- package/dist/cli/loadEnvFiles.js +113 -0
- package/dist/cli/run.js +47 -46
- package/dist/src/DependencyTracker.d.ts +44 -0
- package/dist/src/DependencyTracker.js +239 -0
- package/dist/src/FlinkApp.d.ts +163 -10
- package/dist/src/FlinkApp.js +823 -184
- package/dist/src/FlinkContext.d.ts +41 -0
- package/dist/src/FlinkErrors.d.ts +19 -6
- package/dist/src/FlinkErrors.js +36 -42
- package/dist/src/FlinkHttpHandler.d.ts +157 -18
- package/dist/src/FlinkJob.d.ts +10 -0
- package/dist/src/FlinkLog.d.ts +82 -18
- package/dist/src/FlinkLog.js +165 -13
- package/dist/src/FlinkLogFactory.d.ts +288 -0
- package/dist/src/FlinkLogFactory.js +619 -0
- package/dist/src/FlinkRepo.d.ts +10 -2
- package/dist/src/FlinkRepo.js +11 -1
- package/dist/src/FlinkRequestContext.d.ts +63 -0
- package/dist/src/FlinkRequestContext.js +74 -0
- package/dist/src/FlinkResponse.d.ts +6 -0
- package/dist/src/FlinkService.d.ts +38 -0
- package/dist/src/FlinkService.js +46 -0
- package/dist/src/LeaderElection.d.ts +45 -0
- package/dist/src/LeaderElection.js +269 -0
- package/dist/src/SchemaCache.d.ts +84 -0
- package/dist/src/SchemaCache.js +289 -0
- package/dist/src/TypeScriptCompiler.d.ts +161 -51
- package/dist/src/TypeScriptCompiler.js +1253 -617
- package/dist/src/TypeScriptUtils.js +4 -0
- package/dist/src/ai/AgentRunner.d.ts +39 -0
- package/dist/src/ai/AgentRunner.js +760 -0
- package/dist/src/ai/ConversationAgent.d.ts +279 -0
- package/dist/src/ai/ConversationAgent.js +404 -0
- package/dist/src/ai/ConversationFlinkAgent.d.ts +278 -0
- package/dist/src/ai/ConversationFlinkAgent.js +404 -0
- package/dist/src/ai/FlinkAgent.d.ts +690 -0
- package/dist/src/ai/FlinkAgent.js +729 -0
- package/dist/src/ai/FlinkTool.d.ts +135 -0
- package/dist/src/ai/FlinkTool.js +2 -0
- package/dist/src/ai/InMemoryConversationAgent.d.ts +121 -0
- package/dist/src/ai/InMemoryConversationAgent.js +209 -0
- package/dist/src/ai/LLMAdapter.d.ts +148 -0
- package/dist/src/ai/LLMAdapter.js +2 -0
- package/dist/src/ai/PersistentFlinkAgent.d.ts +278 -0
- package/dist/src/ai/PersistentFlinkAgent.js +403 -0
- package/dist/src/ai/SubAgentExecutor.d.ts +38 -0
- package/dist/src/ai/SubAgentExecutor.js +223 -0
- package/dist/src/ai/ToolExecutor.d.ts +64 -0
- package/dist/src/ai/ToolExecutor.js +497 -0
- package/dist/src/ai/agentInstructions.d.ts +68 -0
- package/dist/src/ai/agentInstructions.js +286 -0
- package/dist/src/ai/index.d.ts +8 -0
- package/dist/src/ai/index.js +26 -0
- package/dist/src/ai/instructionFileLoader.d.ts +44 -0
- package/dist/src/ai/instructionFileLoader.js +179 -0
- package/dist/src/auth/FlinkAuthPlugin.d.ts +1 -1
- package/dist/src/handlers/StreamWriterFactory.d.ts +20 -0
- package/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +17 -0
- package/dist/src/loadPluginSchemas.d.ts +45 -0
- package/dist/src/loadPluginSchemas.js +143 -0
- package/dist/src/schema-extraction/ComplexTypeDetection.d.ts +40 -0
- package/dist/src/schema-extraction/ComplexTypeDetection.js +75 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.d.ts +321 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.js +925 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.spec.d.ts +1 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.spec.js +233 -0
- package/dist/src/schema-extraction/TypeScriptTokenizer.d.ts +57 -0
- package/dist/src/schema-extraction/TypeScriptTokenizer.js +177 -0
- package/dist/src/schema-extraction/index.d.ts +2 -0
- package/dist/src/schema-extraction/index.js +20 -0
- package/dist/src/schema-extraction/types.d.ts +31 -0
- package/dist/src/schema-extraction/types.js +2 -0
- package/dist/src/utils/loadFlinkConfig.d.ts +53 -0
- package/dist/src/utils/loadFlinkConfig.js +77 -0
- package/dist/src/utils.d.ts +30 -0
- package/dist/src/utils.js +52 -0
- package/dist/src/workers/SchemaGeneratorWorker.d.ts +1 -0
- package/dist/src/workers/SchemaGeneratorWorker.js +49 -0
- package/dist/src/workers/WorkerPool.d.ts +60 -0
- package/dist/src/workers/WorkerPool.js +306 -0
- package/examples/logging-hierarchical-example.ts +125 -0
- package/package.json +27 -4
- package/readme.md +499 -0
- package/spec/AgentDescendantDetection.spec.ts +335 -0
- package/spec/AgentDuplicateDetection.spec.ts +112 -0
- package/spec/AgentObserver.spec.ts +266 -0
- package/spec/AgentRunner.spec.ts +1062 -0
- package/spec/AsyncLocalStorageContext.spec.ts +223 -0
- package/spec/ConversationHooks.spec.ts +257 -0
- package/spec/FlinkAgent.spec.ts +681 -0
- package/spec/FlinkApp.htmlResponse.spec.ts +260 -0
- package/spec/FlinkApp.onError.invocation.spec.ts +151 -0
- package/spec/FlinkApp.onError.spec.ts +1 -2
- package/spec/FlinkApp.routeOrdering.spec.ts +61 -0
- package/spec/FlinkApp.undefinedResponse.spec.ts +123 -0
- package/spec/FlinkJob.spec.ts +171 -0
- package/spec/FlinkLogFactory.spec.ts +337 -0
- package/spec/FlinkRepo.spec.ts +1 -1
- package/spec/LeaderElection.spec.ts +174 -0
- package/spec/StreamingIntegration.spec.ts +139 -0
- package/spec/ToolExecutor.spec.ts +465 -0
- package/spec/TypeScriptCompiler.spec.ts +1 -1
- package/spec/TypeScriptSourceParser.spec.ts +1215 -0
- package/spec/TypeScriptTokenizer.spec.ts +366 -0
- package/spec/ai/ContextCompaction.spec.ts +405 -0
- package/spec/ai/ConversationAgent.spec.ts +520 -0
- package/spec/ai/InMemoryConversationAgent.spec.ts +144 -0
- package/spec/ai/agentInstructions.spec.ts +358 -0
- package/spec/fixtures/agent-instructions/TestAgent.ts +24 -0
- package/spec/fixtures/agent-instructions/simple.md +3 -0
- package/spec/fixtures/agent-instructions/template.md +18 -0
- package/spec/fixtures/agent-instructions/yaml-format.yaml +9 -0
- package/spec/mock-project/dist/.tsbuildinfo +1 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar.js +56 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar2.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema.js +52 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema2.js +52 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema3.js +52 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema2.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile2.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler2.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchCar.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOnboardingSession.js +75 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOrderWithComplexTypes.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchProductWithIntersection.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchUserWithUnion.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostCar.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogin.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogout.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PutCar.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/index.js +83 -0
- package/spec/mock-project/dist/spec/mock-project/src/repos/CarRepo.js +26 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/Car.js +2 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/DefaultExportSchema.js +2 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/FileWithTwoSchemas.js +2 -0
- package/spec/mock-project/dist/src/FlinkApp.js +1000 -0
- package/spec/mock-project/dist/src/FlinkContext.js +2 -0
- package/spec/mock-project/dist/src/FlinkErrors.js +143 -0
- package/spec/mock-project/dist/src/FlinkHttpHandler.js +47 -0
- package/spec/mock-project/dist/src/FlinkJob.js +2 -0
- package/spec/mock-project/dist/src/FlinkLog.js +119 -0
- package/spec/mock-project/dist/src/FlinkLogFactory.js +617 -0
- package/spec/mock-project/dist/src/FlinkPlugin.js +2 -0
- package/spec/mock-project/dist/src/FlinkRepo.js +224 -0
- package/spec/mock-project/dist/src/FlinkRequestContext.js +74 -0
- package/spec/mock-project/dist/src/FlinkResponse.js +2 -0
- package/spec/mock-project/dist/src/ai/AgentExecutor.js +279 -0
- package/spec/mock-project/dist/src/ai/AgentRunner.js +632 -0
- package/spec/mock-project/dist/src/ai/ConversationAgent.js +402 -0
- package/spec/mock-project/dist/src/ai/ConversationFlinkAgent.js +422 -0
- package/spec/mock-project/dist/src/ai/FlinkAgent.js +699 -0
- package/spec/mock-project/dist/src/ai/FlinkTool.js +2 -0
- package/spec/mock-project/dist/src/ai/InMemoryConversationAgent.js +209 -0
- package/spec/mock-project/dist/src/ai/LLMAdapter.js +2 -0
- package/spec/mock-project/dist/src/ai/SubAgentExecutor.js +223 -0
- package/spec/mock-project/dist/src/ai/ToolExecutor.js +412 -0
- package/spec/mock-project/dist/src/ai/agentInstructions.js +246 -0
- package/spec/mock-project/dist/src/auth/FlinkAuthPlugin.js +2 -0
- package/spec/mock-project/dist/src/auth/FlinkAuthUser.js +2 -0
- package/spec/mock-project/dist/src/handlers/GetCar.js +26 -52
- package/spec/mock-project/dist/src/handlers/GetCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCar2.js +32 -54
- package/spec/mock-project/dist/src/handlers/GetCar2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js +26 -48
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js +28 -48
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js +29 -48
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js +26 -50
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js +28 -50
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js +27 -53
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js +29 -53
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js +16 -49
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js +25 -50
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchCar.js +27 -53
- package/spec/mock-project/dist/src/handlers/PatchCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchOnboardingSession.js +44 -70
- package/spec/mock-project/dist/src/handlers/PatchOnboardingSession.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchOrderWithComplexTypes.js +27 -53
- package/spec/mock-project/dist/src/handlers/PatchOrderWithComplexTypes.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchProductWithIntersection.js +28 -54
- package/spec/mock-project/dist/src/handlers/PatchProductWithIntersection.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchUserWithUnion.js +28 -54
- package/spec/mock-project/dist/src/handlers/PatchUserWithUnion.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PostCar.js +24 -50
- package/spec/mock-project/dist/src/handlers/PostCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PostLogin.js +25 -51
- package/spec/mock-project/dist/src/handlers/PostLogin.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PostLogout.js +24 -50
- package/spec/mock-project/dist/src/handlers/PostLogout.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PutCar.js +24 -50
- package/spec/mock-project/dist/src/handlers/PutCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/spec/mock-project/dist/src/index.js +52 -76
- package/spec/mock-project/dist/src/index.js.map +1 -0
- package/spec/mock-project/dist/src/mock-data-generator.js +9 -0
- package/spec/mock-project/dist/src/repos/CarRepo.js +12 -24
- package/spec/mock-project/dist/src/repos/CarRepo.js.map +1 -0
- package/spec/mock-project/dist/src/schemas/Car.js +3 -1
- package/spec/mock-project/dist/src/schemas/Car.js.map +1 -0
- package/spec/mock-project/dist/src/schemas/DefaultExportSchema.js +3 -1
- package/spec/mock-project/dist/src/schemas/DefaultExportSchema.js.map +1 -0
- package/spec/mock-project/dist/src/schemas/FileWithTwoSchemas.js +3 -1
- package/spec/mock-project/dist/src/schemas/FileWithTwoSchemas.js.map +1 -0
- package/spec/mock-project/dist/src/utils.js +290 -0
- package/spec/mock-project/tsconfig.json +6 -1
- package/spec/schema-generation-nested-objects.spec.ts +97 -0
- package/spec/testHelpers.ts +49 -0
- package/spec/utils.caseConversion.spec.ts +78 -0
- package/spec/utils.spec.ts +13 -13
- package/src/DependencyTracker.ts +166 -0
- package/src/FlinkApp.ts +895 -154
- package/src/FlinkContext.ts +43 -0
- package/src/FlinkErrors.ts +32 -12
- package/src/FlinkHttpHandler.ts +182 -20
- package/src/FlinkJob.ts +11 -0
- package/src/FlinkLog.ts +119 -12
- package/src/FlinkLogFactory.ts +699 -0
- package/src/FlinkRepo.ts +10 -3
- package/src/FlinkRequestContext.ts +95 -0
- package/src/FlinkResponse.ts +6 -0
- package/src/FlinkService.ts +49 -0
- package/src/LeaderElection.ts +203 -0
- package/src/SchemaCache.ts +232 -0
- package/src/TypeScriptCompiler.ts +1347 -610
- package/src/TypeScriptUtils.ts +5 -0
- package/src/ai/AgentRunner.ts +646 -0
- package/src/ai/ConversationAgent.ts +413 -0
- package/src/ai/FlinkAgent.ts +1069 -0
- package/src/ai/FlinkTool.ts +165 -0
- package/src/ai/InMemoryConversationAgent.ts +149 -0
- package/src/ai/LLMAdapter.ts +126 -0
- package/src/ai/ToolExecutor.ts +485 -0
- package/src/ai/agentInstructions.ts +245 -0
- package/src/ai/index.ts +8 -0
- package/src/ai/instructionFileLoader.ts +156 -0
- package/src/auth/FlinkAuthPlugin.ts +2 -1
- package/src/handlers/StreamWriterFactory.ts +84 -0
- package/src/index.ts +14 -0
- package/src/loadPluginSchemas.ts +141 -0
- package/src/schema-extraction/TypeScriptSourceParser.ts +1058 -0
- package/src/schema-extraction/TypeScriptTokenizer.ts +205 -0
- package/src/schema-extraction/index.ts +2 -0
- package/src/schema-extraction/types.ts +34 -0
- package/src/utils/loadFlinkConfig.ts +89 -0
- package/src/utils.ts +52 -0
- package/tsconfig.json +6 -1
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { FlinkApp } from "../src/FlinkApp";
|
|
2
|
+
import { FlinkContext } from "../src/FlinkContext";
|
|
3
|
+
import { GetHandler, Handler, HttpMethod } from "../src/FlinkHttpHandler";
|
|
4
|
+
|
|
5
|
+
const request = require("supertest");
|
|
6
|
+
|
|
7
|
+
interface TestContext extends FlinkContext {}
|
|
8
|
+
|
|
9
|
+
describe("HTML response handler (html: true in RouteProps)", () => {
|
|
10
|
+
let app: FlinkApp<TestContext>;
|
|
11
|
+
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
if (app && app.started) {
|
|
14
|
+
await app.stop();
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should return text/html content type", async () => {
|
|
19
|
+
const handler: GetHandler<TestContext, string> = async () => {
|
|
20
|
+
return { data: "<h1>Hello</h1>" };
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
app = new FlinkApp<TestContext>({ name: "test-html-ct", port: 4100 });
|
|
24
|
+
await app.start();
|
|
25
|
+
|
|
26
|
+
app.addHandler({
|
|
27
|
+
default: handler,
|
|
28
|
+
Route: { method: HttpMethod.get, path: "/html", responseType: "html" },
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const response = await request(app.expressApp).get("/html");
|
|
32
|
+
|
|
33
|
+
expect(response.status).toBe(200);
|
|
34
|
+
expect(response.headers["content-type"]).toMatch(/text\/html/);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should return the raw HTML string as the body, not a JSON envelope", async () => {
|
|
38
|
+
const html = "<!DOCTYPE html><html><body><h1>Hello</h1></body></html>";
|
|
39
|
+
|
|
40
|
+
const handler: GetHandler<TestContext, string> = async () => {
|
|
41
|
+
return { data: html };
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
app = new FlinkApp<TestContext>({ name: "test-html-body", port: 4101 });
|
|
45
|
+
await app.start();
|
|
46
|
+
|
|
47
|
+
app.addHandler({
|
|
48
|
+
default: handler,
|
|
49
|
+
Route: { method: HttpMethod.get, path: "/html", responseType: "html" },
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const response = await request(app.expressApp).get("/html");
|
|
53
|
+
|
|
54
|
+
expect(response.text).toBe(html);
|
|
55
|
+
// Must NOT be a JSON envelope
|
|
56
|
+
expect(response.body).not.toEqual(jasmine.objectContaining({ data: jasmine.anything() }));
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should respect a custom status code", async () => {
|
|
60
|
+
const handler: GetHandler<TestContext, string> = async () => {
|
|
61
|
+
return { status: 404, data: "<h1>Not Found</h1>" };
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
app = new FlinkApp<TestContext>({ name: "test-html-status", port: 4102 });
|
|
65
|
+
await app.start();
|
|
66
|
+
|
|
67
|
+
app.addHandler({
|
|
68
|
+
default: handler,
|
|
69
|
+
Route: { method: HttpMethod.get, path: "/html", responseType: "html" },
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const response = await request(app.expressApp).get("/html");
|
|
73
|
+
|
|
74
|
+
expect(response.status).toBe(404);
|
|
75
|
+
expect(response.text).toBe("<h1>Not Found</h1>");
|
|
76
|
+
expect(response.headers["content-type"]).toMatch(/text\/html/);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should not affect regular JSON handlers on the same app", async () => {
|
|
80
|
+
const jsonHandler: GetHandler<TestContext, any> = async () => {
|
|
81
|
+
return { status: 200, data: { ok: true } };
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const htmlHandler: GetHandler<TestContext, string> = async () => {
|
|
85
|
+
return { data: "<p>HTML</p>" };
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
app = new FlinkApp<TestContext>({ name: "test-html-coexist", port: 4103 });
|
|
89
|
+
await app.start();
|
|
90
|
+
|
|
91
|
+
app.addHandler({
|
|
92
|
+
default: jsonHandler,
|
|
93
|
+
Route: { method: HttpMethod.get, path: "/json" },
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
app.addHandler({
|
|
97
|
+
default: htmlHandler,
|
|
98
|
+
Route: { method: HttpMethod.get, path: "/html", responseType: "html" },
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const jsonRes = await request(app.expressApp).get("/json");
|
|
102
|
+
expect(jsonRes.status).toBe(200);
|
|
103
|
+
expect(jsonRes.headers["content-type"]).toMatch(/application\/json/);
|
|
104
|
+
expect(jsonRes.body.data).toEqual({ ok: true });
|
|
105
|
+
|
|
106
|
+
const htmlRes = await request(app.expressApp).get("/html");
|
|
107
|
+
expect(htmlRes.status).toBe(200);
|
|
108
|
+
expect(htmlRes.headers["content-type"]).toMatch(/text\/html/);
|
|
109
|
+
expect(htmlRes.text).toBe("<p>HTML</p>");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("should handle POST html handlers", async () => {
|
|
113
|
+
const handler: Handler<TestContext, { name: string }, string> = async ({ req }) => {
|
|
114
|
+
return { data: `<h1>Hello ${req.body.name}</h1>` };
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
app = new FlinkApp<TestContext>({ name: "test-html-post", port: 4104 });
|
|
118
|
+
await app.start();
|
|
119
|
+
|
|
120
|
+
app.addHandler({
|
|
121
|
+
default: handler,
|
|
122
|
+
Route: { method: HttpMethod.post, path: "/html", responseType: "html" },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const response = await request(app.expressApp).post("/html").send({ name: "World" });
|
|
126
|
+
|
|
127
|
+
expect(response.status).toBe(200);
|
|
128
|
+
expect(response.headers["content-type"]).toMatch(/text\/html/);
|
|
129
|
+
expect(response.text).toBe("<h1>Hello World</h1>");
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("Binary Buffer response handler (responseType: image/png)", () => {
|
|
134
|
+
let app: FlinkApp<TestContext>;
|
|
135
|
+
|
|
136
|
+
afterEach(async () => {
|
|
137
|
+
if (app && app.started) {
|
|
138
|
+
await app.stop();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("should return image/png content type for Buffer response", async () => {
|
|
143
|
+
// Minimal valid 1x1 red PNG (67 bytes)
|
|
144
|
+
const pngBuffer = Buffer.from(
|
|
145
|
+
"89504e470d0a1a0a0000000d49484452000000010000000108020000009001" +
|
|
146
|
+
"2e00000000c4944415478016360f8cfc00000000200016934e360000000049454e44ae426082",
|
|
147
|
+
"hex"
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const handler: GetHandler<TestContext, Buffer> = async () => {
|
|
151
|
+
return { data: pngBuffer };
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
app = new FlinkApp<TestContext>({ name: "test-png-ct", port: 4110 });
|
|
155
|
+
await app.start();
|
|
156
|
+
|
|
157
|
+
app.addHandler({
|
|
158
|
+
default: handler,
|
|
159
|
+
Route: { method: HttpMethod.get, path: "/image", responseType: "image/png" },
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const response = await request(app.expressApp).get("/image");
|
|
163
|
+
|
|
164
|
+
expect(response.status).toBe(200);
|
|
165
|
+
expect(response.headers["content-type"]).toMatch(/image\/png/);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should return the exact buffer bytes as the response body", async () => {
|
|
169
|
+
const pngBuffer = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
170
|
+
|
|
171
|
+
const handler: GetHandler<TestContext, Buffer> = async () => {
|
|
172
|
+
return { data: pngBuffer };
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
app = new FlinkApp<TestContext>({ name: "test-png-body", port: 4111 });
|
|
176
|
+
await app.start();
|
|
177
|
+
|
|
178
|
+
app.addHandler({
|
|
179
|
+
default: handler,
|
|
180
|
+
Route: { method: HttpMethod.get, path: "/image", responseType: "image/png" },
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const response = await request(app.expressApp)
|
|
184
|
+
.get("/image")
|
|
185
|
+
.buffer(true)
|
|
186
|
+
.parse((res: any, callback: any) => {
|
|
187
|
+
const chunks: Buffer[] = [];
|
|
188
|
+
res.on("data", (chunk: Buffer) => chunks.push(chunk));
|
|
189
|
+
res.on("end", () => callback(null, Buffer.concat(chunks)));
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(response.status).toBe(200);
|
|
193
|
+
expect(Buffer.compare(response.body, pngBuffer)).toBe(0);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("should respect a custom status code for binary responses", async () => {
|
|
197
|
+
const handler: GetHandler<TestContext, Buffer> = async () => {
|
|
198
|
+
return { status: 404, data: Buffer.from("not found") };
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
app = new FlinkApp<TestContext>({ name: "test-png-status", port: 4112 });
|
|
202
|
+
await app.start();
|
|
203
|
+
|
|
204
|
+
app.addHandler({
|
|
205
|
+
default: handler,
|
|
206
|
+
Route: { method: HttpMethod.get, path: "/image", responseType: "image/png" },
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const response = await request(app.expressApp).get("/image");
|
|
210
|
+
|
|
211
|
+
expect(response.status).toBe(404);
|
|
212
|
+
expect(response.headers["content-type"]).toMatch(/image\/png/);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("should support image/jpeg content type", async () => {
|
|
216
|
+
const jpegBuffer = Buffer.from([0xff, 0xd8, 0xff, 0xe0]);
|
|
217
|
+
|
|
218
|
+
const handler: GetHandler<TestContext, Buffer> = async () => {
|
|
219
|
+
return { data: jpegBuffer };
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
app = new FlinkApp<TestContext>({ name: "test-jpeg-ct", port: 4113 });
|
|
223
|
+
await app.start();
|
|
224
|
+
|
|
225
|
+
app.addHandler({
|
|
226
|
+
default: handler,
|
|
227
|
+
Route: { method: HttpMethod.get, path: "/photo", responseType: "image/jpeg" },
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const response = await request(app.expressApp).get("/photo");
|
|
231
|
+
|
|
232
|
+
expect(response.status).toBe(200);
|
|
233
|
+
expect(response.headers["content-type"]).toMatch(/image\/jpeg/);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("should support application/octet-stream for generic binary data", async () => {
|
|
237
|
+
const binaryData = Buffer.from([0x00, 0x01, 0x02, 0x03, 0xff]);
|
|
238
|
+
|
|
239
|
+
const handler: GetHandler<TestContext, Buffer> = async () => {
|
|
240
|
+
return { data: binaryData };
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
app = new FlinkApp<TestContext>({ name: "test-octet", port: 4114 });
|
|
244
|
+
await app.start();
|
|
245
|
+
|
|
246
|
+
app.addHandler({
|
|
247
|
+
default: handler,
|
|
248
|
+
Route: {
|
|
249
|
+
method: HttpMethod.get,
|
|
250
|
+
path: "/binary",
|
|
251
|
+
responseType: "application/octet-stream",
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const response = await request(app.expressApp).get("/binary");
|
|
256
|
+
|
|
257
|
+
expect(response.status).toBe(200);
|
|
258
|
+
expect(response.headers["content-type"]).toMatch(/application\/octet-stream/);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { FlinkApp } from "../src/FlinkApp";
|
|
2
|
+
import { FlinkContext } from "../src/FlinkContext";
|
|
3
|
+
import { FlinkError } from "../src/FlinkErrors";
|
|
4
|
+
import { GetHandler, Handler, HttpMethod } from "../src/FlinkHttpHandler";
|
|
5
|
+
import { FlinkResponse } from "../src/FlinkResponse";
|
|
6
|
+
|
|
7
|
+
const request = require("supertest");
|
|
8
|
+
|
|
9
|
+
interface TestContext extends FlinkContext {}
|
|
10
|
+
|
|
11
|
+
const reqSchema = {
|
|
12
|
+
type: "object",
|
|
13
|
+
properties: {
|
|
14
|
+
name: { type: "string" },
|
|
15
|
+
},
|
|
16
|
+
required: ["name"],
|
|
17
|
+
additionalProperties: false,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const resSchema = {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
id: { type: "string" },
|
|
24
|
+
},
|
|
25
|
+
required: ["id"],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
describe("FlinkApp onError invocation", () => {
|
|
29
|
+
let app: FlinkApp<TestContext>;
|
|
30
|
+
let calls: { error: FlinkResponse<FlinkError>; context: any }[];
|
|
31
|
+
|
|
32
|
+
const onError = (error: FlinkResponse<FlinkError>, context: any) => {
|
|
33
|
+
calls.push({ error, context });
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
calls = [];
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterEach(async () => {
|
|
41
|
+
if (app && app.started) {
|
|
42
|
+
await app.stop();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should invoke onError for request validation 400s", async () => {
|
|
47
|
+
const handler: Handler<TestContext, any, any> = async () => {
|
|
48
|
+
return { data: { id: "ok" } };
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
app = new FlinkApp<TestContext>({ name: "test-onerror-req", port: 4210, onError });
|
|
52
|
+
await app.start();
|
|
53
|
+
|
|
54
|
+
app.addHandler({
|
|
55
|
+
default: handler,
|
|
56
|
+
Route: { method: HttpMethod.post, path: "/test", reqSchema },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const response = await request(app.expressApp).post("/test").send({ wrong: "field" });
|
|
60
|
+
|
|
61
|
+
expect(response.status).toBe(400);
|
|
62
|
+
expect(calls.length).toBe(1);
|
|
63
|
+
expect(calls[0].error.status).toBe(400);
|
|
64
|
+
expect(calls[0].error.error?.title).toBe("Bad request");
|
|
65
|
+
expect(calls[0].context.method).toBe(HttpMethod.post);
|
|
66
|
+
expect(calls[0].context.path).toBe("/test");
|
|
67
|
+
expect(calls[0].context.reqId).toBeDefined();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should invoke onError for response validation 500s (invalid shape)", async () => {
|
|
71
|
+
const handler: GetHandler<TestContext, any> = async () => {
|
|
72
|
+
return { data: { wrongField: 123 } }; // missing required "id"
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
app = new FlinkApp<TestContext>({ name: "test-onerror-res", port: 4211, onError });
|
|
76
|
+
await app.start();
|
|
77
|
+
|
|
78
|
+
app.addHandler({
|
|
79
|
+
default: handler,
|
|
80
|
+
Route: { method: HttpMethod.get, path: "/test", resSchema },
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const response = await request(app.expressApp).get("/test");
|
|
84
|
+
|
|
85
|
+
expect(response.status).toBe(500);
|
|
86
|
+
expect(calls.length).toBe(1);
|
|
87
|
+
expect(calls[0].error.status).toBe(500);
|
|
88
|
+
expect(calls[0].error.error?.title).toBe("Bad response");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should invoke onError for response validation 500s (no data)", async () => {
|
|
92
|
+
const handler: GetHandler<TestContext, any> = async () => {
|
|
93
|
+
return { status: 200 } as any; // no data
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
app = new FlinkApp<TestContext>({ name: "test-onerror-nodata", port: 4212, onError });
|
|
97
|
+
await app.start();
|
|
98
|
+
|
|
99
|
+
app.addHandler({
|
|
100
|
+
default: handler,
|
|
101
|
+
Route: { method: HttpMethod.get, path: "/test", resSchema },
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const response = await request(app.expressApp).get("/test");
|
|
105
|
+
|
|
106
|
+
expect(response.status).toBe(500);
|
|
107
|
+
expect(calls.length).toBe(1);
|
|
108
|
+
expect(calls[0].error.status).toBe(500);
|
|
109
|
+
expect(calls[0].error.error?.title).toBe("Bad response");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("should still invoke onError for handler-thrown errors", async () => {
|
|
113
|
+
const handler: GetHandler<TestContext, any> = async () => {
|
|
114
|
+
throw new Error("boom");
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
app = new FlinkApp<TestContext>({ name: "test-onerror-throw", port: 4213, onError });
|
|
118
|
+
await app.start();
|
|
119
|
+
|
|
120
|
+
app.addHandler({
|
|
121
|
+
default: handler,
|
|
122
|
+
Route: { method: HttpMethod.get, path: "/test" },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const response = await request(app.expressApp).get("/test");
|
|
126
|
+
|
|
127
|
+
expect(response.status).toBe(500);
|
|
128
|
+
expect(calls.length).toBe(1);
|
|
129
|
+
expect(calls[0].error.status).toBe(500);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should pass the app context (ctx) to the callback", async () => {
|
|
133
|
+
const handler: GetHandler<TestContext, any> = async () => {
|
|
134
|
+
throw new Error("boom");
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
app = new FlinkApp<TestContext>({ name: "test-onerror-ctx", port: 4214, onError });
|
|
138
|
+
await app.start();
|
|
139
|
+
|
|
140
|
+
app.addHandler({
|
|
141
|
+
default: handler,
|
|
142
|
+
Route: { method: HttpMethod.get, path: "/test" },
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
await request(app.expressApp).get("/test");
|
|
146
|
+
|
|
147
|
+
expect(calls.length).toBe(1);
|
|
148
|
+
expect(calls[0].context.ctx).toBeDefined();
|
|
149
|
+
expect(calls[0].context.ctx).toBe(app.ctx);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { FlinkApp } from "../src/FlinkApp";
|
|
2
2
|
import { FlinkContext } from "../src/FlinkContext";
|
|
3
|
-
import { FlinkResponse } from "../src/FlinkResponse";
|
|
4
3
|
import { FlinkError } from "../src/FlinkErrors";
|
|
5
|
-
import { badRequest, internalServerError, notFound } from "../src/FlinkErrors";
|
|
6
4
|
import { HttpMethod } from "../src/FlinkHttpHandler";
|
|
5
|
+
import { FlinkResponse } from "../src/FlinkResponse";
|
|
7
6
|
|
|
8
7
|
interface TestContext extends FlinkContext {}
|
|
9
8
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { FlinkApp, autoRegisteredHandlers } from "../src/FlinkApp";
|
|
2
|
+
import { FlinkContext } from "../src/FlinkContext";
|
|
3
|
+
import { GetHandler, HttpMethod } from "../src/FlinkHttpHandler";
|
|
4
|
+
|
|
5
|
+
const request = require("supertest");
|
|
6
|
+
|
|
7
|
+
interface TestContext extends FlinkContext {}
|
|
8
|
+
|
|
9
|
+
describe("Route ordering", () => {
|
|
10
|
+
let app: FlinkApp<TestContext>;
|
|
11
|
+
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
// Clean up auto-registered handlers between tests
|
|
14
|
+
autoRegisteredHandlers.length = 0;
|
|
15
|
+
|
|
16
|
+
if (app && app.started) {
|
|
17
|
+
await app.stop();
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should match static segment before parameterized segment when parameterized handler is registered first", async () => {
|
|
22
|
+
const byTagsHandler: GetHandler<TestContext, any> = async () => {
|
|
23
|
+
return { status: 200, data: { route: "by-tags" } };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const byIdHandler: GetHandler<TestContext, any> = async ({ req }) => {
|
|
27
|
+
return { status: 200, data: { route: "by-id", id: req.params.id } };
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Register parameterized route first (simulates bad file discovery order)
|
|
31
|
+
autoRegisteredHandlers.push({
|
|
32
|
+
handler: {
|
|
33
|
+
default: byIdHandler,
|
|
34
|
+
Route: { method: HttpMethod.get, path: "/jobs/:id" },
|
|
35
|
+
},
|
|
36
|
+
assumedHttpMethod: HttpMethod.get,
|
|
37
|
+
__file: "GetJobById.ts",
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
autoRegisteredHandlers.push({
|
|
41
|
+
handler: {
|
|
42
|
+
default: byTagsHandler,
|
|
43
|
+
Route: { method: HttpMethod.get, path: "/jobs/by-tags" },
|
|
44
|
+
},
|
|
45
|
+
assumedHttpMethod: HttpMethod.get,
|
|
46
|
+
__file: "GetJobsByTags.ts",
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
app = new FlinkApp<TestContext>({ name: "test-route-order", port: 4050 });
|
|
50
|
+
await app.start();
|
|
51
|
+
|
|
52
|
+
const byTagsRes = await request(app.expressApp).get("/jobs/by-tags");
|
|
53
|
+
expect(byTagsRes.status).toBe(200);
|
|
54
|
+
expect(byTagsRes.body.data.route).toBe("by-tags");
|
|
55
|
+
|
|
56
|
+
const byIdRes = await request(app.expressApp).get("/jobs/abc123");
|
|
57
|
+
expect(byIdRes.status).toBe(200);
|
|
58
|
+
expect(byIdRes.body.data.route).toBe("by-id");
|
|
59
|
+
expect(byIdRes.body.data.id).toBe("abc123");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { FlinkApp } from "../src/FlinkApp";
|
|
2
|
+
import { FlinkContext } from "../src/FlinkContext";
|
|
3
|
+
import { GetHandler, Handler, HttpMethod } from "../src/FlinkHttpHandler";
|
|
4
|
+
|
|
5
|
+
const request = require("supertest");
|
|
6
|
+
|
|
7
|
+
interface TestContext extends FlinkContext {}
|
|
8
|
+
|
|
9
|
+
const resSchema = {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
id: { type: "string" },
|
|
13
|
+
},
|
|
14
|
+
required: ["id"],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe("FlinkApp response validation when handler returns no data", () => {
|
|
18
|
+
let app: FlinkApp<TestContext>;
|
|
19
|
+
|
|
20
|
+
afterEach(async () => {
|
|
21
|
+
if (app && app.started) {
|
|
22
|
+
await app.stop();
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should return 500 bad response when handler returns undefined data with a response schema", async () => {
|
|
27
|
+
const handler: GetHandler<TestContext, any> = async () => {
|
|
28
|
+
return { status: 200 } as any; // no data
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
app = new FlinkApp<TestContext>({ name: "test-undefined-data", port: 4200 });
|
|
32
|
+
await app.start();
|
|
33
|
+
|
|
34
|
+
app.addHandler({
|
|
35
|
+
default: handler,
|
|
36
|
+
Route: { method: HttpMethod.get, path: "/test", resSchema },
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const response = await request(app.expressApp).get("/test");
|
|
40
|
+
|
|
41
|
+
expect(response.status).toBe(500);
|
|
42
|
+
expect(response.body.error.title).toBe("Bad response");
|
|
43
|
+
expect(response.body.error.detail).toContain("no data");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should NOT return 500 when handler returns status 204 with no data (even if schema is defined)", async () => {
|
|
47
|
+
const handler: Handler<TestContext, any, any> = async () => {
|
|
48
|
+
return { status: 204 } as any;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
app = new FlinkApp<TestContext>({ name: "test-204-no-data", port: 4201 });
|
|
52
|
+
await app.start();
|
|
53
|
+
|
|
54
|
+
app.addHandler({
|
|
55
|
+
default: handler,
|
|
56
|
+
Route: { method: HttpMethod.patch, path: "/test", resSchema },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const response = await request(app.expressApp).patch("/test");
|
|
60
|
+
|
|
61
|
+
expect(response.status).toBe(204);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should validate normally when handler returns valid data", async () => {
|
|
65
|
+
const handler: GetHandler<TestContext, any> = async () => {
|
|
66
|
+
return { data: { id: "abc-123" } };
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
app = new FlinkApp<TestContext>({ name: "test-valid-data", port: 4202 });
|
|
70
|
+
await app.start();
|
|
71
|
+
|
|
72
|
+
app.addHandler({
|
|
73
|
+
default: handler,
|
|
74
|
+
Route: { method: HttpMethod.get, path: "/test", resSchema },
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const response = await request(app.expressApp).get("/test");
|
|
78
|
+
|
|
79
|
+
expect(response.status).toBe(200);
|
|
80
|
+
expect(response.body.data.id).toBe("abc-123");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should return 500 bad response when handler returns invalid data against schema", async () => {
|
|
84
|
+
const handler: GetHandler<TestContext, any> = async () => {
|
|
85
|
+
return { data: { wrongField: 123 } }; // missing required "id"
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
app = new FlinkApp<TestContext>({ name: "test-invalid-data", port: 4203 });
|
|
89
|
+
await app.start();
|
|
90
|
+
|
|
91
|
+
app.addHandler({
|
|
92
|
+
default: handler,
|
|
93
|
+
Route: { method: HttpMethod.get, path: "/test", resSchema },
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const response = await request(app.expressApp).get("/test");
|
|
97
|
+
|
|
98
|
+
expect(response.status).toBe(500);
|
|
99
|
+
expect(response.body.error.title).toBe("Bad response");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("should not crash the server when handler returns undefined data", async () => {
|
|
103
|
+
const handler: GetHandler<TestContext, any> = async () => {
|
|
104
|
+
return { status: 200 } as any;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
app = new FlinkApp<TestContext>({ name: "test-no-crash", port: 4204 });
|
|
108
|
+
await app.start();
|
|
109
|
+
|
|
110
|
+
app.addHandler({
|
|
111
|
+
default: handler,
|
|
112
|
+
Route: { method: HttpMethod.get, path: "/test", resSchema },
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// First request triggers the bad response
|
|
116
|
+
await request(app.expressApp).get("/test");
|
|
117
|
+
|
|
118
|
+
// Server should still be running and able to handle further requests
|
|
119
|
+
const second = await request(app.expressApp).get("/test");
|
|
120
|
+
expect(second.status).toBe(500); // still returns 500, but doesn't crash
|
|
121
|
+
expect(app.started).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
});
|