swallowkit 1.0.0-beta.6 → 1.0.0-beta.60
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/README.ja.md +86 -177
- package/README.md +86 -178
- package/dist/__tests__/fixtures.d.ts +12 -0
- package/dist/__tests__/fixtures.d.ts.map +1 -1
- package/dist/__tests__/fixtures.js +101 -0
- package/dist/__tests__/fixtures.js.map +1 -1
- package/dist/cli/commands/add-auth.d.ts +15 -0
- package/dist/cli/commands/add-auth.d.ts.map +1 -0
- package/dist/cli/commands/add-auth.js +790 -0
- package/dist/cli/commands/add-auth.js.map +1 -0
- package/dist/cli/commands/add-connector.d.ts +20 -0
- package/dist/cli/commands/add-connector.d.ts.map +1 -0
- package/dist/cli/commands/add-connector.js +163 -0
- package/dist/cli/commands/add-connector.js.map +1 -0
- package/dist/cli/commands/cosmos-worktree-isolation.d.ts +25 -0
- package/dist/cli/commands/cosmos-worktree-isolation.d.ts.map +1 -0
- package/dist/cli/commands/cosmos-worktree-isolation.js +124 -0
- package/dist/cli/commands/cosmos-worktree-isolation.js.map +1 -0
- package/dist/cli/commands/create-model.d.ts +1 -4
- package/dist/cli/commands/create-model.d.ts.map +1 -1
- package/dist/cli/commands/create-model.js +19 -81
- package/dist/cli/commands/create-model.js.map +1 -1
- package/dist/cli/commands/dev-seeds.d.ts +62 -0
- package/dist/cli/commands/dev-seeds.d.ts.map +1 -0
- package/dist/cli/commands/dev-seeds.js +474 -0
- package/dist/cli/commands/dev-seeds.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +38 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +733 -155
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +3 -1
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts +78 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +1453 -898
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +24 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +473 -90
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts +5 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +333 -55
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +14 -3
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +179 -8
- package/dist/core/config.js.map +1 -1
- package/dist/core/mock/connector-mock-server.d.ts +101 -0
- package/dist/core/mock/connector-mock-server.d.ts.map +1 -0
- package/dist/core/mock/connector-mock-server.js +480 -0
- package/dist/core/mock/connector-mock-server.js.map +1 -0
- package/dist/core/mock/zod-mock-generator.d.ts +14 -0
- package/dist/core/mock/zod-mock-generator.d.ts.map +1 -0
- package/dist/core/mock/zod-mock-generator.js +163 -0
- package/dist/core/mock/zod-mock-generator.js.map +1 -0
- package/dist/core/operations/auth-operations.d.ts +61 -0
- package/dist/core/operations/auth-operations.d.ts.map +1 -0
- package/dist/core/operations/auth-operations.js +272 -0
- package/dist/core/operations/auth-operations.js.map +1 -0
- package/dist/core/operations/create-model.d.ts +15 -0
- package/dist/core/operations/create-model.d.ts.map +1 -0
- package/dist/core/operations/create-model.js +185 -0
- package/dist/core/operations/create-model.js.map +1 -0
- package/dist/core/operations/file-session.d.ts +56 -0
- package/dist/core/operations/file-session.d.ts.map +1 -0
- package/dist/core/operations/file-session.js +203 -0
- package/dist/core/operations/file-session.js.map +1 -0
- package/dist/core/operations/provision-operations.d.ts +52 -0
- package/dist/core/operations/provision-operations.d.ts.map +1 -0
- package/dist/core/operations/provision-operations.js +211 -0
- package/dist/core/operations/provision-operations.js.map +1 -0
- package/dist/core/operations/runtime.d.ts +37 -0
- package/dist/core/operations/runtime.d.ts.map +1 -0
- package/dist/core/operations/runtime.js +248 -0
- package/dist/core/operations/runtime.js.map +1 -0
- package/dist/core/operations/scaffold-apply.d.ts +21 -0
- package/dist/core/operations/scaffold-apply.d.ts.map +1 -0
- package/dist/core/operations/scaffold-apply.js +57 -0
- package/dist/core/operations/scaffold-apply.js.map +1 -0
- package/dist/core/operations/scaffold-machine.d.ts +16 -0
- package/dist/core/operations/scaffold-machine.d.ts.map +1 -0
- package/dist/core/operations/scaffold-machine.js +51 -0
- package/dist/core/operations/scaffold-machine.js.map +1 -0
- package/dist/core/operations/scaffold-plan.d.ts +43 -0
- package/dist/core/operations/scaffold-plan.d.ts.map +1 -0
- package/dist/core/operations/scaffold-plan.js +187 -0
- package/dist/core/operations/scaffold-plan.js.map +1 -0
- package/dist/core/project/agent-instructions.d.ts +29 -0
- package/dist/core/project/agent-instructions.d.ts.map +1 -0
- package/dist/core/project/agent-instructions.js +159 -0
- package/dist/core/project/agent-instructions.js.map +1 -0
- package/dist/core/project/artifacts.d.ts +58 -0
- package/dist/core/project/artifacts.d.ts.map +1 -0
- package/dist/core/project/artifacts.js +179 -0
- package/dist/core/project/artifacts.js.map +1 -0
- package/dist/core/project/boundaries.d.ts +50 -0
- package/dist/core/project/boundaries.d.ts.map +1 -0
- package/dist/core/project/boundaries.js +117 -0
- package/dist/core/project/boundaries.js.map +1 -0
- package/dist/core/project/capabilities.d.ts +58 -0
- package/dist/core/project/capabilities.d.ts.map +1 -0
- package/dist/core/project/capabilities.js +135 -0
- package/dist/core/project/capabilities.js.map +1 -0
- package/dist/core/project/drift.d.ts +29 -0
- package/dist/core/project/drift.d.ts.map +1 -0
- package/dist/core/project/drift.js +188 -0
- package/dist/core/project/drift.js.map +1 -0
- package/dist/core/project/infra.d.ts +49 -0
- package/dist/core/project/infra.d.ts.map +1 -0
- package/dist/core/project/infra.js +167 -0
- package/dist/core/project/infra.js.map +1 -0
- package/dist/core/project/manifest.d.ts +92 -0
- package/dist/core/project/manifest.d.ts.map +1 -0
- package/dist/core/project/manifest.js +321 -0
- package/dist/core/project/manifest.js.map +1 -0
- package/dist/core/project/state.d.ts +15 -0
- package/dist/core/project/state.d.ts.map +1 -0
- package/dist/core/project/state.js +106 -0
- package/dist/core/project/state.js.map +1 -0
- package/dist/core/project/validation.d.ts +20 -0
- package/dist/core/project/validation.d.ts.map +1 -0
- package/dist/core/project/validation.js +274 -0
- package/dist/core/project/validation.js.map +1 -0
- package/dist/core/project/workflows.d.ts +43 -0
- package/dist/core/project/workflows.d.ts.map +1 -0
- package/dist/core/project/workflows.js +299 -0
- package/dist/core/project/workflows.js.map +1 -0
- package/dist/core/scaffold/auth-generator.d.ts +63 -0
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -0
- package/dist/core/scaffold/auth-generator.js +1774 -0
- package/dist/core/scaffold/auth-generator.js.map +1 -0
- package/dist/core/scaffold/connector-functions-generator.d.ts +41 -0
- package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -0
- package/dist/core/scaffold/connector-functions-generator.js +1029 -0
- package/dist/core/scaffold/connector-functions-generator.js.map +1 -0
- package/dist/core/scaffold/functions-generator.d.ts +7 -1
- package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
- package/dist/core/scaffold/functions-generator.js +771 -69
- package/dist/core/scaffold/functions-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts +21 -1
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +243 -43
- package/dist/core/scaffold/model-parser.js.map +1 -1
- package/dist/core/scaffold/native-schema-generator.d.ts +19 -0
- package/dist/core/scaffold/native-schema-generator.d.ts.map +1 -0
- package/dist/core/scaffold/native-schema-generator.js +753 -0
- package/dist/core/scaffold/native-schema-generator.js.map +1 -0
- package/dist/core/scaffold/nextjs-generator.d.ts +8 -0
- package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
- package/dist/core/scaffold/nextjs-generator.js +153 -9
- package/dist/core/scaffold/nextjs-generator.js.map +1 -1
- package/dist/core/scaffold/openapi-generator.d.ts +3 -0
- package/dist/core/scaffold/openapi-generator.d.ts.map +1 -0
- package/dist/core/scaffold/openapi-generator.js +190 -0
- package/dist/core/scaffold/openapi-generator.js.map +1 -0
- package/dist/core/scaffold/ui-generator.d.ts +17 -4
- package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
- package/dist/core/scaffold/ui-generator.js +174 -34
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/core/verify/index.d.ts +69 -0
- package/dist/core/verify/index.d.ts.map +1 -0
- package/dist/core/verify/index.js +412 -0
- package/dist/core/verify/index.js.map +1 -0
- package/dist/database/base-model.d.ts +3 -3
- package/dist/database/base-model.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/machine/contracts.d.ts +24 -0
- package/dist/machine/contracts.d.ts.map +1 -0
- package/dist/machine/contracts.js +3 -0
- package/dist/machine/contracts.js.map +1 -0
- package/dist/machine/errors.d.ts +14 -0
- package/dist/machine/errors.d.ts.map +1 -0
- package/dist/machine/errors.js +42 -0
- package/dist/machine/errors.js.map +1 -0
- package/dist/machine/index.d.ts +3 -0
- package/dist/machine/index.d.ts.map +1 -0
- package/dist/machine/index.js +566 -0
- package/dist/machine/index.js.map +1 -0
- package/dist/mcp/index.d.ts +25 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +425 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/types/index.d.ts +109 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/package-manager.d.ts +5 -2
- package/dist/utils/package-manager.d.ts.map +1 -1
- package/dist/utils/package-manager.js +30 -9
- package/dist/utils/package-manager.js.map +1 -1
- package/dist/utils/python-uv.d.ts +21 -0
- package/dist/utils/python-uv.d.ts.map +1 -0
- package/dist/utils/python-uv.js +111 -0
- package/dist/utils/python-uv.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +49 -0
- package/dist/version.js.map +1 -0
- package/package.json +9 -5
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +1139 -0
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +206 -0
- package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +532 -0
- package/src/__tests__/agent-instructions.test.ts +237 -0
- package/src/__tests__/agent-loop-phase34.test.ts +513 -0
- package/src/__tests__/auth.test.ts +770 -0
- package/src/__tests__/bff-swa-auth.test.ts +275 -0
- package/src/__tests__/config.test.ts +295 -0
- package/src/__tests__/connector-functions-generator.test.ts +288 -0
- package/src/__tests__/connector-mock-server.test.ts +439 -0
- package/src/__tests__/connector-model-bff.test.ts +162 -0
- package/src/__tests__/cosmos-worktree-isolation.test.ts +149 -0
- package/src/__tests__/dev-seeds.test.ts +236 -0
- package/src/__tests__/dev.test.ts +296 -0
- package/src/__tests__/file-session.test.ts +98 -0
- package/src/__tests__/fixtures.ts +184 -0
- package/src/__tests__/functions-generator.test.ts +237 -0
- package/src/__tests__/init.test.ts +411 -0
- package/src/__tests__/jest-globals.d.ts +8 -0
- package/src/__tests__/machine.test.ts +371 -0
- package/src/__tests__/mcp.test.ts +253 -0
- package/src/__tests__/model-parser.test.ts +81 -0
- package/src/__tests__/multi-auth.test.ts +160 -0
- package/src/__tests__/nextjs-generator.test.ts +105 -0
- package/src/__tests__/openapi-generator.test.ts +43 -0
- package/src/__tests__/package-manager.test.ts +227 -0
- package/src/__tests__/plan-apply.test.ts +499 -0
- package/src/__tests__/python-uv.test.ts +48 -0
- package/src/__tests__/scaffold.test.ts +302 -0
- package/src/__tests__/string-utils.test.ts +75 -0
- package/src/__tests__/tsconfig.json +15 -0
- package/src/__tests__/ui-generator.test.ts +248 -0
- package/src/__tests__/zod-mock-generator.test.ts +132 -0
- package/src/cli/commands/add-auth.ts +853 -0
- package/src/cli/commands/add-connector.ts +158 -0
- package/src/cli/commands/cosmos-worktree-isolation.ts +130 -0
- package/src/cli/commands/create-model.ts +62 -0
- package/src/cli/commands/dev-seeds.ts +629 -0
- package/src/cli/commands/dev.ts +1252 -0
- package/src/cli/commands/index.ts +9 -0
- package/src/cli/commands/init.ts +3541 -0
- package/src/cli/commands/provision.ts +193 -0
- package/src/cli/commands/scaffold.ts +1081 -0
- package/src/cli/index.ts +350 -0
- package/src/core/config.ts +380 -0
- package/src/core/mock/connector-mock-server.ts +555 -0
- package/src/core/mock/zod-mock-generator.ts +205 -0
- package/src/core/operations/auth-operations.ts +360 -0
- package/src/core/operations/create-model.ts +191 -0
- package/src/core/operations/file-session.ts +222 -0
- package/src/core/operations/provision-operations.ts +297 -0
- package/src/core/operations/runtime.ts +258 -0
- package/src/core/operations/scaffold-apply.ts +106 -0
- package/src/core/operations/scaffold-machine.ts +77 -0
- package/src/core/operations/scaffold-plan.ts +215 -0
- package/src/core/project/agent-instructions.ts +167 -0
- package/src/core/project/agent-templates/AGENTS.md +502 -0
- package/src/core/project/agent-templates/CLAUDE.md +31 -0
- package/src/core/project/agent-templates/azure-functions.instructions.md +22 -0
- package/src/core/project/agent-templates/bff-routes.instructions.md +18 -0
- package/src/core/project/agent-templates/copilot-instructions.md +27 -0
- package/src/core/project/agent-templates/shared-models.instructions.md +72 -0
- package/src/core/project/artifacts.ts +205 -0
- package/src/core/project/boundaries.ts +170 -0
- package/src/core/project/capabilities.ts +193 -0
- package/src/core/project/drift.ts +202 -0
- package/src/core/project/infra.ts +196 -0
- package/src/core/project/manifest.ts +402 -0
- package/src/core/project/state.ts +77 -0
- package/src/core/project/validation.ts +295 -0
- package/src/core/project/workflows.ts +295 -0
- package/src/core/scaffold/auth-generator.ts +1824 -0
- package/src/core/scaffold/connector-functions-generator.ts +1133 -0
- package/src/core/scaffold/functions-generator.ts +972 -0
- package/src/core/scaffold/model-parser.ts +854 -0
- package/src/core/scaffold/native-schema-generator.ts +904 -0
- package/src/core/scaffold/nextjs-generator.ts +382 -0
- package/src/core/scaffold/openapi-generator.ts +212 -0
- package/src/core/scaffold/ui-generator.ts +1100 -0
- package/src/core/verify/index.ts +468 -0
- package/src/database/base-model.ts +184 -0
- package/src/database/client.ts +140 -0
- package/src/database/repository.ts +104 -0
- package/src/database/runtime-check.ts +25 -0
- package/src/index.ts +27 -0
- package/src/machine/contracts.ts +33 -0
- package/src/machine/errors.ts +45 -0
- package/src/machine/index.ts +690 -0
- package/src/mcp/index.ts +423 -0
- package/src/types/index.ts +182 -0
- package/src/utils/package-manager.ts +246 -0
- package/src/utils/python-uv.ts +96 -0
- package/src/version.ts +13 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import {
|
|
3
|
+
generateBFFCallFunctionWithMultipleAuth,
|
|
4
|
+
generateBFFCallFunctionWithSwaAuth,
|
|
5
|
+
} from "../core/scaffold/auth-generator";
|
|
6
|
+
|
|
7
|
+
type GeneratedModule = {
|
|
8
|
+
callFunction(config: { method: "GET"; path: string }): Promise<unknown>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type FetchCall = {
|
|
12
|
+
url: string;
|
|
13
|
+
init: RequestInit | undefined;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const originalFetch = global.fetch;
|
|
17
|
+
const originalBaseUrl = process.env.BACKEND_FUNCTIONS_BASE_URL;
|
|
18
|
+
const originalFunctionsKey = process.env.BACKEND_FUNCTIONS_KEY;
|
|
19
|
+
|
|
20
|
+
function encodePrincipal(value: unknown): string {
|
|
21
|
+
return Buffer.from(JSON.stringify(value), "utf8").toString("base64");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function decodeForwardedPrincipal(call: FetchCall): Record<string, unknown> | undefined {
|
|
25
|
+
const value = (call.init?.headers as Record<string, string>)["x-ms-client-principal"];
|
|
26
|
+
return value
|
|
27
|
+
? JSON.parse(Buffer.from(value, "base64").toString("utf8"))
|
|
28
|
+
: undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function loadGeneratedModule(
|
|
32
|
+
code: string,
|
|
33
|
+
requestHeaders: Record<string, string>,
|
|
34
|
+
): GeneratedModule {
|
|
35
|
+
const output = ts.transpileModule(code, {
|
|
36
|
+
compilerOptions: {
|
|
37
|
+
esModuleInterop: true,
|
|
38
|
+
module: ts.ModuleKind.CommonJS,
|
|
39
|
+
target: ts.ScriptTarget.ES2020,
|
|
40
|
+
},
|
|
41
|
+
reportDiagnostics: true,
|
|
42
|
+
});
|
|
43
|
+
expect(output.diagnostics).toEqual([]);
|
|
44
|
+
|
|
45
|
+
class MockNextResponse {
|
|
46
|
+
static json(body: unknown, init?: { status?: number }) {
|
|
47
|
+
return { body, status: init?.status ?? 200 };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
constructor(
|
|
51
|
+
public body: unknown,
|
|
52
|
+
public init?: { status?: number },
|
|
53
|
+
) {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const generatedModule = { exports: {} as GeneratedModule };
|
|
57
|
+
const mockRequire = (id: string): unknown => {
|
|
58
|
+
if (id === "next/server") return { NextResponse: MockNextResponse };
|
|
59
|
+
if (id === "next/headers") {
|
|
60
|
+
return { headers: async () => new Headers(requestHeaders) };
|
|
61
|
+
}
|
|
62
|
+
if (id === "zod/v4") return require("zod/v4");
|
|
63
|
+
throw new Error(`Unexpected generated import: ${id}`);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
new Function("require", "module", "exports", output.outputText)(
|
|
67
|
+
mockRequire,
|
|
68
|
+
generatedModule,
|
|
69
|
+
generatedModule.exports,
|
|
70
|
+
);
|
|
71
|
+
return generatedModule.exports;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function runGeneratedCall(options: {
|
|
75
|
+
code: string;
|
|
76
|
+
requestHeaders: Record<string, string>;
|
|
77
|
+
mePrincipal?: unknown;
|
|
78
|
+
functionsBaseUrl?: string;
|
|
79
|
+
}): Promise<FetchCall[]> {
|
|
80
|
+
process.env.BACKEND_FUNCTIONS_BASE_URL =
|
|
81
|
+
options.functionsBaseUrl ?? "https://functions.example.test";
|
|
82
|
+
process.env.BACKEND_FUNCTIONS_KEY = "functions-key-secret";
|
|
83
|
+
|
|
84
|
+
const calls: FetchCall[] = [];
|
|
85
|
+
global.fetch = jest.fn(async (input: string | URL | Request, init?: RequestInit) => {
|
|
86
|
+
const url = input.toString();
|
|
87
|
+
calls.push({ url, init });
|
|
88
|
+
if (url.endsWith("/.auth/me")) {
|
|
89
|
+
return {
|
|
90
|
+
ok: true,
|
|
91
|
+
status: 200,
|
|
92
|
+
json: async () => ({ clientPrincipal: options.mePrincipal }),
|
|
93
|
+
} as Response;
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
ok: true,
|
|
97
|
+
status: 200,
|
|
98
|
+
json: async () => ({ ok: true }),
|
|
99
|
+
text: async () => "",
|
|
100
|
+
} as Response;
|
|
101
|
+
}) as typeof fetch;
|
|
102
|
+
|
|
103
|
+
const generated = loadGeneratedModule(options.code, options.requestHeaders);
|
|
104
|
+
await generated.callFunction({ method: "GET", path: "/api/resource" });
|
|
105
|
+
return calls;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function functionsCall(calls: FetchCall[]): FetchCall {
|
|
109
|
+
const call = calls.find(({ url }) => url.endsWith("/api/resource"));
|
|
110
|
+
expect(call).toBeDefined();
|
|
111
|
+
return call!;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
afterEach(() => {
|
|
115
|
+
global.fetch = originalFetch;
|
|
116
|
+
if (originalBaseUrl === undefined) delete process.env.BACKEND_FUNCTIONS_BASE_URL;
|
|
117
|
+
else process.env.BACKEND_FUNCTIONS_BASE_URL = originalBaseUrl;
|
|
118
|
+
if (originalFunctionsKey === undefined) delete process.env.BACKEND_FUNCTIONS_KEY;
|
|
119
|
+
else process.env.BACKEND_FUNCTIONS_KEY = originalFunctionsKey;
|
|
120
|
+
jest.restoreAllMocks();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe("generated BFF SWA credential forwarding", () => {
|
|
124
|
+
const validPrincipal = {
|
|
125
|
+
identityProvider: "github",
|
|
126
|
+
userId: "user-123",
|
|
127
|
+
userDetails: "octocat",
|
|
128
|
+
userRoles: ["anonymous", "authenticated", 123],
|
|
129
|
+
untrustedExtra: "must-not-be-forwarded",
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
it("normalizes an incoming principal and does not call /.auth/me", async () => {
|
|
133
|
+
const calls = await runGeneratedCall({
|
|
134
|
+
code: generateBFFCallFunctionWithSwaAuth(),
|
|
135
|
+
requestHeaders: {
|
|
136
|
+
cookie: "session=cookie-secret",
|
|
137
|
+
host: "app.example.test",
|
|
138
|
+
"x-ms-client-principal": encodePrincipal(validPrincipal),
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
expect(calls.map(({ url }) => url)).not.toContain("https://app.example.test/.auth/me");
|
|
143
|
+
expect(decodeForwardedPrincipal(functionsCall(calls))).toEqual({
|
|
144
|
+
identityProvider: "github",
|
|
145
|
+
userId: "user-123",
|
|
146
|
+
userDetails: "octocat",
|
|
147
|
+
userRoles: ["anonymous", "authenticated"],
|
|
148
|
+
});
|
|
149
|
+
expect(
|
|
150
|
+
(functionsCall(calls).init?.headers as Record<string, string>)[
|
|
151
|
+
"x-functions-key"
|
|
152
|
+
],
|
|
153
|
+
).toBe("functions-key-secret");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("uses x-forwarded-host for the /.auth/me fallback", async () => {
|
|
157
|
+
const calls = await runGeneratedCall({
|
|
158
|
+
code: generateBFFCallFunctionWithSwaAuth(),
|
|
159
|
+
requestHeaders: {
|
|
160
|
+
cookie: "session=valid",
|
|
161
|
+
host: "internal.example.test",
|
|
162
|
+
"x-forwarded-host": "public.example.test",
|
|
163
|
+
},
|
|
164
|
+
mePrincipal: validPrincipal,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
expect(calls[0].url).toBe("https://public.example.test/.auth/me");
|
|
168
|
+
expect(decodeForwardedPrincipal(functionsCall(calls))).toEqual({
|
|
169
|
+
identityProvider: "github",
|
|
170
|
+
userId: "user-123",
|
|
171
|
+
userDetails: "octocat",
|
|
172
|
+
userRoles: ["anonymous", "authenticated"],
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("falls back to host when x-forwarded-host is absent", async () => {
|
|
177
|
+
const calls = await runGeneratedCall({
|
|
178
|
+
code: generateBFFCallFunctionWithSwaAuth(),
|
|
179
|
+
requestHeaders: { cookie: "session=valid", host: "app.example.test" },
|
|
180
|
+
mePrincipal: validPrincipal,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
expect(calls[0].url).toBe("https://app.example.test/.auth/me");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("uses the first comma-separated forwarded host and protocol", async () => {
|
|
187
|
+
const calls = await runGeneratedCall({
|
|
188
|
+
code: generateBFFCallFunctionWithSwaAuth(),
|
|
189
|
+
requestHeaders: {
|
|
190
|
+
cookie: "session=valid",
|
|
191
|
+
host: "internal.example.test",
|
|
192
|
+
"x-forwarded-host": "public.example.test, proxy.example.test",
|
|
193
|
+
"x-forwarded-proto": "http, https",
|
|
194
|
+
},
|
|
195
|
+
mePrincipal: validPrincipal,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
expect(calls[0].url).toBe("http://public.example.test/.auth/me");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it.each([
|
|
202
|
+
["a missing userId", { ...validPrincipal, userId: undefined }],
|
|
203
|
+
["a missing authenticated role", { ...validPrincipal, userRoles: ["anonymous"] }],
|
|
204
|
+
["a missing identityProvider", { ...validPrincipal, identityProvider: "" }],
|
|
205
|
+
])("does not forward a principal with %s", async (_case, principal) => {
|
|
206
|
+
const calls = await runGeneratedCall({
|
|
207
|
+
code: generateBFFCallFunctionWithSwaAuth(),
|
|
208
|
+
requestHeaders: { "x-ms-client-principal": encodePrincipal(principal) },
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
expect(decodeForwardedPrincipal(functionsCall(calls))).toBeUndefined();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it.each([
|
|
215
|
+
"not-base64!",
|
|
216
|
+
`${encodePrincipal(validPrincipal)}!`,
|
|
217
|
+
Buffer.from("not-json", "utf8").toString("base64"),
|
|
218
|
+
])(
|
|
219
|
+
"fails closed without logging credentials for malformed principal %s",
|
|
220
|
+
async (principal) => {
|
|
221
|
+
const error = jest.spyOn(console, "error").mockImplementation(() => undefined);
|
|
222
|
+
const cookie = "session=highly-sensitive-cookie";
|
|
223
|
+
const calls = await runGeneratedCall({
|
|
224
|
+
code: generateBFFCallFunctionWithSwaAuth(),
|
|
225
|
+
requestHeaders: { cookie, "x-ms-client-principal": principal },
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
expect(decodeForwardedPrincipal(functionsCall(calls))).toBeUndefined();
|
|
229
|
+
const logged = JSON.stringify(error.mock.calls);
|
|
230
|
+
expect(logged).not.toContain(cookie);
|
|
231
|
+
expect(logged).not.toContain(principal);
|
|
232
|
+
},
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
it("preserves Bearer and applies the same SWA rules for multiple auth", async () => {
|
|
236
|
+
const calls = await runGeneratedCall({
|
|
237
|
+
code: generateBFFCallFunctionWithMultipleAuth(),
|
|
238
|
+
requestHeaders: {
|
|
239
|
+
authorization: "Bearer line-token-secret",
|
|
240
|
+
"x-ms-client-principal": encodePrincipal(validPrincipal),
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
const headers = functionsCall(calls).init?.headers as Record<string, string>;
|
|
244
|
+
|
|
245
|
+
expect(headers.Authorization).toBe("Bearer line-token-secret");
|
|
246
|
+
expect(decodeForwardedPrincipal(functionsCall(calls))?.userId).toBe("user-123");
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("does not add Bearer forwarding to the SWA-only variant", async () => {
|
|
250
|
+
const code = generateBFFCallFunctionWithSwaAuth();
|
|
251
|
+
const calls = await runGeneratedCall({
|
|
252
|
+
code,
|
|
253
|
+
requestHeaders: {
|
|
254
|
+
authorization: "Bearer must-not-be-forwarded",
|
|
255
|
+
"x-ms-client-principal": encodePrincipal(validPrincipal),
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
const headers = functionsCall(calls).init?.headers as Record<string, string>;
|
|
259
|
+
|
|
260
|
+
expect(headers.Authorization).toBeUndefined();
|
|
261
|
+
expect(code).not.toContain("reqHeaders.get('authorization')");
|
|
262
|
+
expect(decodeForwardedPrincipal(functionsCall(calls))?.userId).toBe("user-123");
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("defaults /.auth/me to http for local Functions development", async () => {
|
|
266
|
+
const calls = await runGeneratedCall({
|
|
267
|
+
code: generateBFFCallFunctionWithSwaAuth(),
|
|
268
|
+
requestHeaders: { cookie: "session=valid", host: "localhost:3000" },
|
|
269
|
+
mePrincipal: validPrincipal,
|
|
270
|
+
functionsBaseUrl: "http://localhost:7071",
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
expect(calls[0].url).toBe("http://localhost:3000/.auth/me");
|
|
274
|
+
});
|
|
275
|
+
});
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { validateConfig, loadConfigFromEnv, normalizeAuthConfig } from "../core/config";
|
|
2
|
+
import { SwallowKitConfig } from "../types";
|
|
3
|
+
|
|
4
|
+
describe("validateConfig", () => {
|
|
5
|
+
it("normalizes a legacy provider as the default scheme", () => {
|
|
6
|
+
const auth = normalizeAuthConfig({ provider: "external-token", authorization: { defaultPolicy: "authenticated" } });
|
|
7
|
+
expect(auth?.schemes.default.provider).toBe("external-token");
|
|
8
|
+
expect(auth?.authorization.defaultPolicy).toBe("authenticated");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("accepts SWA and external token named schemes", () => {
|
|
12
|
+
const result = validateConfig({ auth: { schemes: { admin: { provider: "swa" }, lineUser: { provider: "external-token" } }, authorization: { defaultPolicy: "anonymous", policies: { adminOnly: { schemes: ["admin"] }, lineUserOnly: { schemes: ["lineUser"] } } } } });
|
|
13
|
+
expect(result).toEqual({ valid: true, errors: [] });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("rejects undefined scheme references and ambiguous bearer schemes", () => {
|
|
17
|
+
const missing = validateConfig({ auth: { schemes: { admin: { provider: "swa" } }, authorization: { defaultPolicy: "anonymous", policies: { bad: { schemes: ["missing"] } } } } });
|
|
18
|
+
expect(missing.errors.join("\n")).toContain("auth.authorization.policies.bad.schemes");
|
|
19
|
+
const ambiguous = validateConfig({ auth: { schemes: { a: { provider: "external-token" }, b: { provider: "external-token" } }, authorization: { defaultPolicy: "anonymous", policies: { bad: { schemes: ["a", "b"] } } } } });
|
|
20
|
+
expect(ambiguous.errors.join("\n")).toContain("deterministic");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("normalizes public to anonymous", () => {
|
|
24
|
+
expect(normalizeAuthConfig({ provider: "none", authorization: { defaultPolicy: "public" } })?.authorization.defaultPolicy).toBe("anonymous");
|
|
25
|
+
});
|
|
26
|
+
it("returns valid for a complete config", () => {
|
|
27
|
+
const config: SwallowKitConfig = {
|
|
28
|
+
database: { connectionString: "AccountEndpoint=https://..." },
|
|
29
|
+
backend: { language: "typescript" },
|
|
30
|
+
api: { endpoint: "/api/_swallowkit" },
|
|
31
|
+
};
|
|
32
|
+
const result = validateConfig(config);
|
|
33
|
+
expect(result.valid).toBe(true);
|
|
34
|
+
expect(result.errors).toHaveLength(0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("accepts an empty database object", () => {
|
|
38
|
+
const config: SwallowKitConfig = {
|
|
39
|
+
database: {},
|
|
40
|
+
backend: { language: "typescript" },
|
|
41
|
+
api: { endpoint: "/api/_swallowkit" },
|
|
42
|
+
};
|
|
43
|
+
const result = validateConfig(config);
|
|
44
|
+
expect(result.valid).toBe(true);
|
|
45
|
+
expect(result.errors).not.toContain("Cosmos DB connection string is required");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("returns error when database settings are incomplete", () => {
|
|
49
|
+
const config: SwallowKitConfig = {
|
|
50
|
+
database: { databaseName: "SwallowKitDB" },
|
|
51
|
+
backend: { language: "typescript" },
|
|
52
|
+
api: { endpoint: "/api/_swallowkit" },
|
|
53
|
+
};
|
|
54
|
+
const result = validateConfig(config);
|
|
55
|
+
expect(result.valid).toBe(false);
|
|
56
|
+
expect(result.errors).toContain("Cosmos DB connection string is required");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("returns error when endpoint does not start with /", () => {
|
|
60
|
+
const config: SwallowKitConfig = {
|
|
61
|
+
database: { connectionString: "AccountEndpoint=https://..." },
|
|
62
|
+
backend: { language: "typescript" },
|
|
63
|
+
api: { endpoint: "api/_swallowkit" },
|
|
64
|
+
};
|
|
65
|
+
const result = validateConfig(config);
|
|
66
|
+
expect(result.valid).toBe(false);
|
|
67
|
+
expect(result.errors).toContain("API endpoint must start with '/'");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("returns multiple errors for multiple issues", () => {
|
|
71
|
+
const config: SwallowKitConfig = {
|
|
72
|
+
database: { databaseName: "SwallowKitDB" },
|
|
73
|
+
backend: { language: "typescript" },
|
|
74
|
+
api: { endpoint: "bad-endpoint" },
|
|
75
|
+
};
|
|
76
|
+
const result = validateConfig(config);
|
|
77
|
+
expect(result.valid).toBe(false);
|
|
78
|
+
expect(result.errors).toHaveLength(2);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("returns error when backend language is invalid", () => {
|
|
82
|
+
const config: SwallowKitConfig = {
|
|
83
|
+
backend: { language: "ruby" as never },
|
|
84
|
+
};
|
|
85
|
+
const result = validateConfig(config);
|
|
86
|
+
expect(result.valid).toBe(false);
|
|
87
|
+
expect(result.errors).toContain("Backend language must be one of: typescript, csharp, python");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("accepts config without database or api (no validation errors for absent sections)", () => {
|
|
91
|
+
const config: SwallowKitConfig = {};
|
|
92
|
+
const result = validateConfig(config);
|
|
93
|
+
// database is undefined → no connectionString check triggered
|
|
94
|
+
expect(result.errors.filter((e) => e.includes("endpoint"))).toHaveLength(0);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// ─── Connector Validation ───────────────────────────────────
|
|
98
|
+
|
|
99
|
+
it("validates valid RDB connector config", () => {
|
|
100
|
+
const config: SwallowKitConfig = {
|
|
101
|
+
connectors: {
|
|
102
|
+
mysql: {
|
|
103
|
+
type: "rdb",
|
|
104
|
+
provider: "mysql",
|
|
105
|
+
connectionEnvVar: "MYSQL_CONNECTION_STRING",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
const result = validateConfig(config);
|
|
110
|
+
expect(result.valid).toBe(true);
|
|
111
|
+
expect(result.errors).toHaveLength(0);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("validates valid API connector config", () => {
|
|
115
|
+
const config: SwallowKitConfig = {
|
|
116
|
+
connectors: {
|
|
117
|
+
backlog: {
|
|
118
|
+
type: "api",
|
|
119
|
+
baseUrlEnvVar: "BACKLOG_API_BASE_URL",
|
|
120
|
+
auth: {
|
|
121
|
+
type: "apiKey",
|
|
122
|
+
envVar: "BACKLOG_API_KEY",
|
|
123
|
+
placement: "query",
|
|
124
|
+
paramName: "apiKey",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
const result = validateConfig(config);
|
|
130
|
+
expect(result.valid).toBe(true);
|
|
131
|
+
expect(result.errors).toHaveLength(0);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("returns error for invalid connector type", () => {
|
|
135
|
+
const config = {
|
|
136
|
+
connectors: {
|
|
137
|
+
bad: {
|
|
138
|
+
type: "graphql",
|
|
139
|
+
baseUrlEnvVar: "GRAPHQL_URL",
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
} as unknown as SwallowKitConfig;
|
|
143
|
+
const result = validateConfig(config);
|
|
144
|
+
expect(result.valid).toBe(false);
|
|
145
|
+
expect(result.errors[0]).toContain("Connector 'bad'");
|
|
146
|
+
expect(result.errors[0]).toContain("type must be one of");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("returns error for RDB connector without provider", () => {
|
|
150
|
+
const config: SwallowKitConfig = {
|
|
151
|
+
connectors: {
|
|
152
|
+
mydb: {
|
|
153
|
+
type: "rdb",
|
|
154
|
+
provider: "" as any,
|
|
155
|
+
connectionEnvVar: "CONN",
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
const result = validateConfig(config);
|
|
160
|
+
expect(result.valid).toBe(false);
|
|
161
|
+
expect(result.errors).toEqual(
|
|
162
|
+
expect.arrayContaining([expect.stringContaining("provider must be one of")])
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("returns error for RDB connector without connectionEnvVar", () => {
|
|
167
|
+
const config: SwallowKitConfig = {
|
|
168
|
+
connectors: {
|
|
169
|
+
mydb: {
|
|
170
|
+
type: "rdb",
|
|
171
|
+
provider: "mysql",
|
|
172
|
+
connectionEnvVar: "",
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
const result = validateConfig(config);
|
|
177
|
+
expect(result.valid).toBe(false);
|
|
178
|
+
expect(result.errors).toEqual(
|
|
179
|
+
expect.arrayContaining([expect.stringContaining("connectionEnvVar is required")])
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("returns error for API connector without baseUrlEnvVar", () => {
|
|
184
|
+
const config: SwallowKitConfig = {
|
|
185
|
+
connectors: {
|
|
186
|
+
ext: {
|
|
187
|
+
type: "api",
|
|
188
|
+
baseUrlEnvVar: "",
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
const result = validateConfig(config);
|
|
193
|
+
expect(result.valid).toBe(false);
|
|
194
|
+
expect(result.errors).toEqual(
|
|
195
|
+
expect.arrayContaining([expect.stringContaining("baseUrlEnvVar is required")])
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("returns error for API connector with invalid auth type", () => {
|
|
200
|
+
const config: SwallowKitConfig = {
|
|
201
|
+
connectors: {
|
|
202
|
+
ext: {
|
|
203
|
+
type: "api",
|
|
204
|
+
baseUrlEnvVar: "EXT_URL",
|
|
205
|
+
auth: {
|
|
206
|
+
type: "saml" as any,
|
|
207
|
+
envVar: "KEY",
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
const result = validateConfig(config);
|
|
213
|
+
expect(result.valid).toBe(false);
|
|
214
|
+
expect(result.errors).toEqual(
|
|
215
|
+
expect.arrayContaining([expect.stringContaining("auth.type must be one of")])
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("validates multiple connectors at once", () => {
|
|
220
|
+
const config: SwallowKitConfig = {
|
|
221
|
+
connectors: {
|
|
222
|
+
mysql: {
|
|
223
|
+
type: "rdb",
|
|
224
|
+
provider: "mysql",
|
|
225
|
+
connectionEnvVar: "MYSQL_CONN",
|
|
226
|
+
},
|
|
227
|
+
backlog: {
|
|
228
|
+
type: "api",
|
|
229
|
+
baseUrlEnvVar: "BACKLOG_URL",
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
const result = validateConfig(config);
|
|
234
|
+
expect(result.valid).toBe(true);
|
|
235
|
+
expect(result.errors).toHaveLength(0);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe("loadConfigFromEnv", () => {
|
|
240
|
+
const originalEnv = process.env;
|
|
241
|
+
|
|
242
|
+
beforeEach(() => {
|
|
243
|
+
process.env = { ...originalEnv };
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
afterAll(() => {
|
|
247
|
+
process.env = originalEnv;
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("returns empty config when no env vars set", () => {
|
|
251
|
+
delete process.env.SWALLOWKIT_DB_CONNECTION_STRING;
|
|
252
|
+
delete process.env.SWALLOWKIT_DB_NAME;
|
|
253
|
+
delete process.env.SWALLOWKIT_API_ENDPOINT;
|
|
254
|
+
|
|
255
|
+
const config = loadConfigFromEnv();
|
|
256
|
+
expect(config.database).toBeUndefined();
|
|
257
|
+
expect(config.api).toBeUndefined();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("reads database connection string from env", () => {
|
|
261
|
+
process.env.SWALLOWKIT_DB_CONNECTION_STRING = "AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=xxx;";
|
|
262
|
+
const config = loadConfigFromEnv();
|
|
263
|
+
expect(config.database?.connectionString).toBe(
|
|
264
|
+
"AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=xxx;"
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("reads database name from env", () => {
|
|
269
|
+
process.env.SWALLOWKIT_DB_NAME = "MyTestDB";
|
|
270
|
+
const config = loadConfigFromEnv();
|
|
271
|
+
expect(config.database?.databaseName).toBe("MyTestDB");
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("reads API endpoint from env", () => {
|
|
275
|
+
process.env.SWALLOWKIT_API_ENDPOINT = "/api/custom";
|
|
276
|
+
const config = loadConfigFromEnv();
|
|
277
|
+
expect(config.api?.endpoint).toBe("/api/custom");
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("reads backend language from env", () => {
|
|
281
|
+
process.env.SWALLOWKIT_BACKEND_LANGUAGE = "python";
|
|
282
|
+
const config = loadConfigFromEnv();
|
|
283
|
+
expect(config.backend?.language).toBe("python");
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("reads all env vars together", () => {
|
|
287
|
+
process.env.SWALLOWKIT_DB_CONNECTION_STRING = "conn";
|
|
288
|
+
process.env.SWALLOWKIT_DB_NAME = "db";
|
|
289
|
+
process.env.SWALLOWKIT_API_ENDPOINT = "/api/v2";
|
|
290
|
+
const config = loadConfigFromEnv();
|
|
291
|
+
expect(config.database?.connectionString).toBe("conn");
|
|
292
|
+
expect(config.database?.databaseName).toBe("db");
|
|
293
|
+
expect(config.api?.endpoint).toBe("/api/v2");
|
|
294
|
+
});
|
|
295
|
+
});
|