swallowkit 1.0.0-beta.2 → 1.0.0-beta.20

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.
Files changed (191) hide show
  1. package/LICENSE +21 -21
  2. package/README.ja.md +312 -215
  3. package/README.md +369 -216
  4. package/dist/__tests__/fixtures.d.ts +22 -0
  5. package/dist/__tests__/fixtures.d.ts.map +1 -0
  6. package/dist/__tests__/fixtures.js +146 -0
  7. package/dist/__tests__/fixtures.js.map +1 -0
  8. package/dist/cli/commands/add-auth.d.ts +10 -0
  9. package/dist/cli/commands/add-auth.d.ts.map +1 -0
  10. package/dist/cli/commands/add-auth.js +444 -0
  11. package/dist/cli/commands/add-auth.js.map +1 -0
  12. package/dist/cli/commands/add-connector.d.ts +20 -0
  13. package/dist/cli/commands/add-connector.d.ts.map +1 -0
  14. package/dist/cli/commands/add-connector.js +163 -0
  15. package/dist/cli/commands/add-connector.js.map +1 -0
  16. package/dist/cli/commands/create-model.d.ts +1 -4
  17. package/dist/cli/commands/create-model.d.ts.map +1 -1
  18. package/dist/cli/commands/create-model.js +21 -82
  19. package/dist/cli/commands/create-model.js.map +1 -1
  20. package/dist/cli/commands/dev-seeds.d.ts +35 -0
  21. package/dist/cli/commands/dev-seeds.d.ts.map +1 -0
  22. package/dist/cli/commands/dev-seeds.js +292 -0
  23. package/dist/cli/commands/dev-seeds.js.map +1 -0
  24. package/dist/cli/commands/dev.d.ts +19 -0
  25. package/dist/cli/commands/dev.d.ts.map +1 -1
  26. package/dist/cli/commands/dev.js +476 -117
  27. package/dist/cli/commands/dev.js.map +1 -1
  28. package/dist/cli/commands/index.d.ts +1 -0
  29. package/dist/cli/commands/index.d.ts.map +1 -1
  30. package/dist/cli/commands/index.js +3 -1
  31. package/dist/cli/commands/index.js.map +1 -1
  32. package/dist/cli/commands/init.d.ts +13 -0
  33. package/dist/cli/commands/init.d.ts.map +1 -1
  34. package/dist/cli/commands/init.js +2627 -1708
  35. package/dist/cli/commands/init.js.map +1 -1
  36. package/dist/cli/commands/scaffold.d.ts +3 -0
  37. package/dist/cli/commands/scaffold.d.ts.map +1 -1
  38. package/dist/cli/commands/scaffold.js +617 -129
  39. package/dist/cli/commands/scaffold.js.map +1 -1
  40. package/dist/cli/index.d.ts +4 -1
  41. package/dist/cli/index.d.ts.map +1 -1
  42. package/dist/cli/index.js +162 -42
  43. package/dist/cli/index.js.map +1 -1
  44. package/dist/core/config.d.ts +8 -2
  45. package/dist/core/config.d.ts.map +1 -1
  46. package/dist/core/config.js +90 -4
  47. package/dist/core/config.js.map +1 -1
  48. package/dist/core/mock/connector-mock-server.d.ts +101 -0
  49. package/dist/core/mock/connector-mock-server.d.ts.map +1 -0
  50. package/dist/core/mock/connector-mock-server.js +480 -0
  51. package/dist/core/mock/connector-mock-server.js.map +1 -0
  52. package/dist/core/mock/zod-mock-generator.d.ts +14 -0
  53. package/dist/core/mock/zod-mock-generator.d.ts.map +1 -0
  54. package/dist/core/mock/zod-mock-generator.js +163 -0
  55. package/dist/core/mock/zod-mock-generator.js.map +1 -0
  56. package/dist/core/operations/create-model.d.ts +15 -0
  57. package/dist/core/operations/create-model.d.ts.map +1 -0
  58. package/dist/core/operations/create-model.js +171 -0
  59. package/dist/core/operations/create-model.js.map +1 -0
  60. package/dist/core/operations/runtime.d.ts +32 -0
  61. package/dist/core/operations/runtime.d.ts.map +1 -0
  62. package/dist/core/operations/runtime.js +225 -0
  63. package/dist/core/operations/runtime.js.map +1 -0
  64. package/dist/core/operations/scaffold-machine.d.ts +16 -0
  65. package/dist/core/operations/scaffold-machine.d.ts.map +1 -0
  66. package/dist/core/operations/scaffold-machine.js +63 -0
  67. package/dist/core/operations/scaffold-machine.js.map +1 -0
  68. package/dist/core/project/manifest.d.ts +92 -0
  69. package/dist/core/project/manifest.d.ts.map +1 -0
  70. package/dist/core/project/manifest.js +321 -0
  71. package/dist/core/project/manifest.js.map +1 -0
  72. package/dist/core/project/validation.d.ts +20 -0
  73. package/dist/core/project/validation.d.ts.map +1 -0
  74. package/dist/core/project/validation.js +204 -0
  75. package/dist/core/project/validation.js.map +1 -0
  76. package/dist/core/scaffold/auth-generator.d.ts +38 -0
  77. package/dist/core/scaffold/auth-generator.d.ts.map +1 -0
  78. package/dist/core/scaffold/auth-generator.js +1244 -0
  79. package/dist/core/scaffold/auth-generator.js.map +1 -0
  80. package/dist/core/scaffold/connector-functions-generator.d.ts +41 -0
  81. package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -0
  82. package/dist/core/scaffold/connector-functions-generator.js +1027 -0
  83. package/dist/core/scaffold/connector-functions-generator.js.map +1 -0
  84. package/dist/core/scaffold/functions-generator.d.ts +7 -1
  85. package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
  86. package/dist/core/scaffold/functions-generator.js +920 -213
  87. package/dist/core/scaffold/functions-generator.js.map +1 -1
  88. package/dist/core/scaffold/model-parser.d.ts +20 -1
  89. package/dist/core/scaffold/model-parser.d.ts.map +1 -1
  90. package/dist/core/scaffold/model-parser.js +329 -135
  91. package/dist/core/scaffold/model-parser.js.map +1 -1
  92. package/dist/core/scaffold/nextjs-generator.d.ts +8 -0
  93. package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
  94. package/dist/core/scaffold/nextjs-generator.js +314 -182
  95. package/dist/core/scaffold/nextjs-generator.js.map +1 -1
  96. package/dist/core/scaffold/openapi-generator.d.ts +3 -0
  97. package/dist/core/scaffold/openapi-generator.d.ts.map +1 -0
  98. package/dist/core/scaffold/openapi-generator.js +190 -0
  99. package/dist/core/scaffold/openapi-generator.js.map +1 -0
  100. package/dist/core/scaffold/ui-generator.d.ts +10 -4
  101. package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
  102. package/dist/core/scaffold/ui-generator.js +768 -663
  103. package/dist/core/scaffold/ui-generator.js.map +1 -1
  104. package/dist/database/base-model.d.ts +3 -3
  105. package/dist/database/base-model.js +3 -3
  106. package/dist/index.d.ts +2 -2
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +2 -1
  109. package/dist/index.js.map +1 -1
  110. package/dist/machine/contracts.d.ts +16 -0
  111. package/dist/machine/contracts.d.ts.map +1 -0
  112. package/dist/machine/contracts.js +3 -0
  113. package/dist/machine/contracts.js.map +1 -0
  114. package/dist/machine/errors.d.ts +11 -0
  115. package/dist/machine/errors.d.ts.map +1 -0
  116. package/dist/machine/errors.js +34 -0
  117. package/dist/machine/errors.js.map +1 -0
  118. package/dist/machine/index.d.ts +3 -0
  119. package/dist/machine/index.d.ts.map +1 -0
  120. package/dist/machine/index.js +156 -0
  121. package/dist/machine/index.js.map +1 -0
  122. package/dist/mcp/index.d.ts +25 -0
  123. package/dist/mcp/index.d.ts.map +1 -0
  124. package/dist/mcp/index.js +184 -0
  125. package/dist/mcp/index.js.map +1 -0
  126. package/dist/types/index.d.ts +65 -0
  127. package/dist/types/index.d.ts.map +1 -1
  128. package/dist/utils/package-manager.d.ts +109 -0
  129. package/dist/utils/package-manager.d.ts.map +1 -0
  130. package/dist/utils/package-manager.js +215 -0
  131. package/dist/utils/package-manager.js.map +1 -0
  132. package/package.json +85 -73
  133. package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +1139 -0
  134. package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +194 -0
  135. package/src/__tests__/__snapshots__/ui-generator.test.ts.snap +532 -0
  136. package/src/__tests__/auth.test.ts +654 -0
  137. package/src/__tests__/config.test.ts +263 -0
  138. package/src/__tests__/connector-functions-generator.test.ts +288 -0
  139. package/src/__tests__/connector-mock-server.test.ts +439 -0
  140. package/src/__tests__/connector-model-bff.test.ts +162 -0
  141. package/src/__tests__/dev-seeds.test.ts +112 -0
  142. package/src/__tests__/dev.test.ts +148 -0
  143. package/src/__tests__/fixtures.ts +144 -0
  144. package/src/__tests__/functions-generator.test.ts +237 -0
  145. package/src/__tests__/init.test.ts +80 -0
  146. package/src/__tests__/machine.test.ts +212 -0
  147. package/src/__tests__/mcp.test.ts +56 -0
  148. package/src/__tests__/model-parser.test.ts +72 -0
  149. package/src/__tests__/nextjs-generator.test.ts +97 -0
  150. package/src/__tests__/openapi-generator.test.ts +43 -0
  151. package/src/__tests__/package-manager.test.ts +189 -0
  152. package/src/__tests__/scaffold.test.ts +39 -0
  153. package/src/__tests__/string-utils.test.ts +75 -0
  154. package/src/__tests__/ui-generator.test.ts +144 -0
  155. package/src/__tests__/zod-mock-generator.test.ts +132 -0
  156. package/src/cli/commands/add-auth.ts +500 -0
  157. package/src/cli/commands/add-connector.ts +158 -0
  158. package/src/cli/commands/create-model.ts +62 -0
  159. package/src/cli/commands/dev-seeds.ts +358 -0
  160. package/src/cli/commands/dev.ts +962 -0
  161. package/src/cli/commands/index.ts +9 -0
  162. package/src/cli/commands/init.ts +3371 -0
  163. package/src/cli/commands/provision.ts +193 -0
  164. package/src/cli/commands/scaffold.ts +1211 -0
  165. package/src/cli/index.ts +191 -0
  166. package/src/core/config.ts +308 -0
  167. package/src/core/mock/connector-mock-server.ts +555 -0
  168. package/src/core/mock/zod-mock-generator.ts +205 -0
  169. package/src/core/operations/create-model.ts +174 -0
  170. package/src/core/operations/runtime.ts +235 -0
  171. package/src/core/operations/scaffold-machine.ts +91 -0
  172. package/src/core/project/manifest.ts +402 -0
  173. package/src/core/project/validation.ts +221 -0
  174. package/src/core/scaffold/auth-generator.ts +1284 -0
  175. package/src/core/scaffold/connector-functions-generator.ts +1128 -0
  176. package/src/core/scaffold/functions-generator.ts +970 -0
  177. package/src/core/scaffold/model-parser.ts +841 -0
  178. package/src/core/scaffold/nextjs-generator.ts +370 -0
  179. package/src/core/scaffold/openapi-generator.ts +212 -0
  180. package/src/core/scaffold/ui-generator.ts +1061 -0
  181. package/src/database/base-model.ts +184 -0
  182. package/src/database/client.ts +140 -0
  183. package/src/database/repository.ts +104 -0
  184. package/src/database/runtime-check.ts +25 -0
  185. package/src/index.ts +27 -0
  186. package/src/machine/contracts.ts +17 -0
  187. package/src/machine/errors.ts +34 -0
  188. package/src/machine/index.ts +173 -0
  189. package/src/mcp/index.ts +185 -0
  190. package/src/types/index.ts +134 -0
  191. package/src/utils/package-manager.ts +229 -0
@@ -0,0 +1,263 @@
1
+ import { validateConfig, loadConfigFromEnv } from "../core/config";
2
+ import { SwallowKitConfig } from "../types";
3
+
4
+ describe("validateConfig", () => {
5
+ it("returns valid for a complete config", () => {
6
+ const config: SwallowKitConfig = {
7
+ database: { connectionString: "AccountEndpoint=https://..." },
8
+ backend: { language: "typescript" },
9
+ api: { endpoint: "/api/_swallowkit" },
10
+ };
11
+ const result = validateConfig(config);
12
+ expect(result.valid).toBe(true);
13
+ expect(result.errors).toHaveLength(0);
14
+ });
15
+
16
+ it("returns error when connectionString is missing", () => {
17
+ const config: SwallowKitConfig = {
18
+ database: {},
19
+ backend: { language: "typescript" },
20
+ api: { endpoint: "/api/_swallowkit" },
21
+ };
22
+ const result = validateConfig(config);
23
+ expect(result.valid).toBe(false);
24
+ expect(result.errors).toContain("Cosmos DB connection string is required");
25
+ });
26
+
27
+ it("returns error when endpoint does not start with /", () => {
28
+ const config: SwallowKitConfig = {
29
+ database: { connectionString: "AccountEndpoint=https://..." },
30
+ backend: { language: "typescript" },
31
+ api: { endpoint: "api/_swallowkit" },
32
+ };
33
+ const result = validateConfig(config);
34
+ expect(result.valid).toBe(false);
35
+ expect(result.errors).toContain("API endpoint must start with '/'");
36
+ });
37
+
38
+ it("returns multiple errors for multiple issues", () => {
39
+ const config: SwallowKitConfig = {
40
+ database: {},
41
+ backend: { language: "typescript" },
42
+ api: { endpoint: "bad-endpoint" },
43
+ };
44
+ const result = validateConfig(config);
45
+ expect(result.valid).toBe(false);
46
+ expect(result.errors).toHaveLength(2);
47
+ });
48
+
49
+ it("returns error when backend language is invalid", () => {
50
+ const config: SwallowKitConfig = {
51
+ backend: { language: "ruby" as never },
52
+ };
53
+ const result = validateConfig(config);
54
+ expect(result.valid).toBe(false);
55
+ expect(result.errors).toContain("Backend language must be one of: typescript, csharp, python");
56
+ });
57
+
58
+ it("accepts config without database or api (no validation errors for absent sections)", () => {
59
+ const config: SwallowKitConfig = {};
60
+ const result = validateConfig(config);
61
+ // database is undefined → no connectionString check triggered
62
+ expect(result.errors.filter((e) => e.includes("endpoint"))).toHaveLength(0);
63
+ });
64
+
65
+ // ─── Connector Validation ───────────────────────────────────
66
+
67
+ it("validates valid RDB connector config", () => {
68
+ const config: SwallowKitConfig = {
69
+ connectors: {
70
+ mysql: {
71
+ type: "rdb",
72
+ provider: "mysql",
73
+ connectionEnvVar: "MYSQL_CONNECTION_STRING",
74
+ },
75
+ },
76
+ };
77
+ const result = validateConfig(config);
78
+ expect(result.valid).toBe(true);
79
+ expect(result.errors).toHaveLength(0);
80
+ });
81
+
82
+ it("validates valid API connector config", () => {
83
+ const config: SwallowKitConfig = {
84
+ connectors: {
85
+ backlog: {
86
+ type: "api",
87
+ baseUrlEnvVar: "BACKLOG_API_BASE_URL",
88
+ auth: {
89
+ type: "apiKey",
90
+ envVar: "BACKLOG_API_KEY",
91
+ placement: "query",
92
+ paramName: "apiKey",
93
+ },
94
+ },
95
+ },
96
+ };
97
+ const result = validateConfig(config);
98
+ expect(result.valid).toBe(true);
99
+ expect(result.errors).toHaveLength(0);
100
+ });
101
+
102
+ it("returns error for invalid connector type", () => {
103
+ const config = {
104
+ connectors: {
105
+ bad: {
106
+ type: "graphql",
107
+ baseUrlEnvVar: "GRAPHQL_URL",
108
+ },
109
+ },
110
+ } as unknown as SwallowKitConfig;
111
+ const result = validateConfig(config);
112
+ expect(result.valid).toBe(false);
113
+ expect(result.errors[0]).toContain("Connector 'bad'");
114
+ expect(result.errors[0]).toContain("type must be one of");
115
+ });
116
+
117
+ it("returns error for RDB connector without provider", () => {
118
+ const config: SwallowKitConfig = {
119
+ connectors: {
120
+ mydb: {
121
+ type: "rdb",
122
+ provider: "" as any,
123
+ connectionEnvVar: "CONN",
124
+ },
125
+ },
126
+ };
127
+ const result = validateConfig(config);
128
+ expect(result.valid).toBe(false);
129
+ expect(result.errors).toEqual(
130
+ expect.arrayContaining([expect.stringContaining("provider must be one of")])
131
+ );
132
+ });
133
+
134
+ it("returns error for RDB connector without connectionEnvVar", () => {
135
+ const config: SwallowKitConfig = {
136
+ connectors: {
137
+ mydb: {
138
+ type: "rdb",
139
+ provider: "mysql",
140
+ connectionEnvVar: "",
141
+ },
142
+ },
143
+ };
144
+ const result = validateConfig(config);
145
+ expect(result.valid).toBe(false);
146
+ expect(result.errors).toEqual(
147
+ expect.arrayContaining([expect.stringContaining("connectionEnvVar is required")])
148
+ );
149
+ });
150
+
151
+ it("returns error for API connector without baseUrlEnvVar", () => {
152
+ const config: SwallowKitConfig = {
153
+ connectors: {
154
+ ext: {
155
+ type: "api",
156
+ baseUrlEnvVar: "",
157
+ },
158
+ },
159
+ };
160
+ const result = validateConfig(config);
161
+ expect(result.valid).toBe(false);
162
+ expect(result.errors).toEqual(
163
+ expect.arrayContaining([expect.stringContaining("baseUrlEnvVar is required")])
164
+ );
165
+ });
166
+
167
+ it("returns error for API connector with invalid auth type", () => {
168
+ const config: SwallowKitConfig = {
169
+ connectors: {
170
+ ext: {
171
+ type: "api",
172
+ baseUrlEnvVar: "EXT_URL",
173
+ auth: {
174
+ type: "saml" as any,
175
+ envVar: "KEY",
176
+ },
177
+ },
178
+ },
179
+ };
180
+ const result = validateConfig(config);
181
+ expect(result.valid).toBe(false);
182
+ expect(result.errors).toEqual(
183
+ expect.arrayContaining([expect.stringContaining("auth.type must be one of")])
184
+ );
185
+ });
186
+
187
+ it("validates multiple connectors at once", () => {
188
+ const config: SwallowKitConfig = {
189
+ connectors: {
190
+ mysql: {
191
+ type: "rdb",
192
+ provider: "mysql",
193
+ connectionEnvVar: "MYSQL_CONN",
194
+ },
195
+ backlog: {
196
+ type: "api",
197
+ baseUrlEnvVar: "BACKLOG_URL",
198
+ },
199
+ },
200
+ };
201
+ const result = validateConfig(config);
202
+ expect(result.valid).toBe(true);
203
+ expect(result.errors).toHaveLength(0);
204
+ });
205
+ });
206
+
207
+ describe("loadConfigFromEnv", () => {
208
+ const originalEnv = process.env;
209
+
210
+ beforeEach(() => {
211
+ process.env = { ...originalEnv };
212
+ });
213
+
214
+ afterAll(() => {
215
+ process.env = originalEnv;
216
+ });
217
+
218
+ it("returns empty config when no env vars set", () => {
219
+ delete process.env.SWALLOWKIT_DB_CONNECTION_STRING;
220
+ delete process.env.SWALLOWKIT_DB_NAME;
221
+ delete process.env.SWALLOWKIT_API_ENDPOINT;
222
+
223
+ const config = loadConfigFromEnv();
224
+ expect(config.database).toBeUndefined();
225
+ expect(config.api).toBeUndefined();
226
+ });
227
+
228
+ it("reads database connection string from env", () => {
229
+ process.env.SWALLOWKIT_DB_CONNECTION_STRING = "AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=xxx;";
230
+ const config = loadConfigFromEnv();
231
+ expect(config.database?.connectionString).toBe(
232
+ "AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=xxx;"
233
+ );
234
+ });
235
+
236
+ it("reads database name from env", () => {
237
+ process.env.SWALLOWKIT_DB_NAME = "MyTestDB";
238
+ const config = loadConfigFromEnv();
239
+ expect(config.database?.databaseName).toBe("MyTestDB");
240
+ });
241
+
242
+ it("reads API endpoint from env", () => {
243
+ process.env.SWALLOWKIT_API_ENDPOINT = "/api/custom";
244
+ const config = loadConfigFromEnv();
245
+ expect(config.api?.endpoint).toBe("/api/custom");
246
+ });
247
+
248
+ it("reads backend language from env", () => {
249
+ process.env.SWALLOWKIT_BACKEND_LANGUAGE = "python";
250
+ const config = loadConfigFromEnv();
251
+ expect(config.backend?.language).toBe("python");
252
+ });
253
+
254
+ it("reads all env vars together", () => {
255
+ process.env.SWALLOWKIT_DB_CONNECTION_STRING = "conn";
256
+ process.env.SWALLOWKIT_DB_NAME = "db";
257
+ process.env.SWALLOWKIT_API_ENDPOINT = "/api/v2";
258
+ const config = loadConfigFromEnv();
259
+ expect(config.database?.connectionString).toBe("conn");
260
+ expect(config.database?.databaseName).toBe("db");
261
+ expect(config.api?.endpoint).toBe("/api/v2");
262
+ });
263
+ });
@@ -0,0 +1,288 @@
1
+ /**
2
+ * コネクタ Functions ジェネレーターのテスト
3
+ */
4
+
5
+ import {
6
+ generateRdbConnectorFunctionTS,
7
+ generateApiConnectorFunctionTS,
8
+ generateRdbConnectorFunctionCSharp,
9
+ generateApiConnectorFunctionCSharp,
10
+ generateRdbConnectorFunctionPython,
11
+ generateApiConnectorFunctionPython,
12
+ isReadOnlyConnector,
13
+ } from "../core/scaffold/connector-functions-generator";
14
+ import {
15
+ createRdbConnectorModelInfo,
16
+ createApiConnectorModelInfo,
17
+ } from "./fixtures";
18
+ import {
19
+ RdbConnectorConfig,
20
+ ApiConnectorConfig,
21
+ RdbModelConnectorConfig,
22
+ ApiModelConnectorConfig,
23
+ } from "../types";
24
+
25
+ // ─── Shared Test Data ────────────────────────────────────────
26
+
27
+ const mysqlConnector: RdbConnectorConfig = {
28
+ type: "rdb",
29
+ provider: "mysql",
30
+ connectionEnvVar: "MYSQL_CONNECTION_STRING",
31
+ };
32
+
33
+ const postgresConnector: RdbConnectorConfig = {
34
+ type: "rdb",
35
+ provider: "postgres",
36
+ connectionEnvVar: "PG_CONNECTION_STRING",
37
+ };
38
+
39
+ const sqlserverConnector: RdbConnectorConfig = {
40
+ type: "rdb",
41
+ provider: "sqlserver",
42
+ connectionEnvVar: "MSSQL_CONNECTION_STRING",
43
+ };
44
+
45
+ const backlogConnector: ApiConnectorConfig = {
46
+ type: "api",
47
+ baseUrlEnvVar: "BACKLOG_API_BASE_URL",
48
+ auth: {
49
+ type: "apiKey",
50
+ envVar: "BACKLOG_API_KEY",
51
+ placement: "query",
52
+ paramName: "apiKey",
53
+ },
54
+ };
55
+
56
+ const bearerConnector: ApiConnectorConfig = {
57
+ type: "api",
58
+ baseUrlEnvVar: "EXT_API_BASE_URL",
59
+ auth: {
60
+ type: "bearer",
61
+ envVar: "EXT_API_TOKEN",
62
+ },
63
+ };
64
+
65
+ // ─── isReadOnlyConnector ────────────────────────────────────
66
+
67
+ describe("isReadOnlyConnector", () => {
68
+ it("returns true when only read operations are present", () => {
69
+ expect(isReadOnlyConnector(["getAll", "getById"])).toBe(true);
70
+ expect(isReadOnlyConnector(["getAll"])).toBe(true);
71
+ expect(isReadOnlyConnector(["getById"])).toBe(true);
72
+ });
73
+
74
+ it("returns false when write operations are present", () => {
75
+ expect(isReadOnlyConnector(["getAll", "getById", "create"])).toBe(false);
76
+ expect(isReadOnlyConnector(["getAll", "update"])).toBe(false);
77
+ expect(isReadOnlyConnector(["getAll", "delete"])).toBe(false);
78
+ });
79
+ });
80
+
81
+ // ─── TypeScript RDB Connector ───────────────────────────────
82
+
83
+ describe("generateRdbConnectorFunctionTS", () => {
84
+ const model = createRdbConnectorModelInfo();
85
+ const modelConnector = model.connectorConfig as RdbModelConnectorConfig;
86
+
87
+ it("generates MySQL read-only functions", () => {
88
+ const code = generateRdbConnectorFunctionTS(model, "@myapp/shared", mysqlConnector, modelConnector);
89
+ expect(code).toContain("import mysql from 'mysql2/promise'");
90
+ expect(code).toContain("MYSQL_CONNECTION_STRING");
91
+ expect(code).toContain("SELECT * FROM users");
92
+ expect(code).toContain("user-get-all");
93
+ expect(code).toContain("user-get-by-id");
94
+ // Should NOT contain write operations
95
+ expect(code).not.toContain("user-create");
96
+ expect(code).not.toContain("user-update");
97
+ expect(code).not.toContain("user-delete");
98
+ });
99
+
100
+ it("generates PostgreSQL functions", () => {
101
+ const code = generateRdbConnectorFunctionTS(model, "@myapp/shared", postgresConnector, modelConnector);
102
+ expect(code).toContain("import pg from 'pg'");
103
+ expect(code).toContain("PG_CONNECTION_STRING");
104
+ });
105
+
106
+ it("generates SQL Server functions", () => {
107
+ const code = generateRdbConnectorFunctionTS(model, "@myapp/shared", sqlserverConnector, modelConnector);
108
+ expect(code).toContain("import sql from 'mssql'");
109
+ expect(code).toContain("MSSQL_CONNECTION_STRING");
110
+ });
111
+
112
+ it("uses correct table and id column", () => {
113
+ const code = generateRdbConnectorFunctionTS(model, "@myapp/shared", mysqlConnector, modelConnector);
114
+ expect(code).toContain("FROM users");
115
+ expect(code).toContain("WHERE id = ");
116
+ });
117
+
118
+ it("imports schema from shared package", () => {
119
+ const code = generateRdbConnectorFunctionTS(model, "@myapp/shared", mysqlConnector, modelConnector);
120
+ expect(code).toContain("from '@myapp/shared'");
121
+ });
122
+ });
123
+
124
+ // ─── TypeScript API Connector ───────────────────────────────
125
+
126
+ describe("generateApiConnectorFunctionTS", () => {
127
+ const model = createApiConnectorModelInfo();
128
+ const modelConnector = model.connectorConfig as ApiModelConnectorConfig;
129
+
130
+ it("generates read-write functions with apiKey auth", () => {
131
+ const code = generateApiConnectorFunctionTS(model, "@myapp/shared", backlogConnector, modelConnector);
132
+ expect(code).toContain("BACKLOG_API_BASE_URL");
133
+ expect(code).toContain("backlogIssue-get-all");
134
+ expect(code).toContain("backlogIssue-get-by-id");
135
+ expect(code).toContain("backlogIssue-create");
136
+ expect(code).toContain("backlogIssue-update");
137
+ });
138
+
139
+ it("generates bearer auth helper", () => {
140
+ const code = generateApiConnectorFunctionTS(model, "@myapp/shared", bearerConnector, modelConnector);
141
+ expect(code).toContain("Authorization");
142
+ expect(code).toContain("Bearer");
143
+ expect(code).toContain("EXT_API_TOKEN");
144
+ });
145
+
146
+ it("does not generate delete when not in operations", () => {
147
+ const code = generateApiConnectorFunctionTS(model, "@myapp/shared", backlogConnector, modelConnector);
148
+ expect(code).not.toContain("backlogIssue-delete");
149
+ });
150
+
151
+ it("generates read-only API connector when operations are limited", () => {
152
+ const readOnlyModel = createApiConnectorModelInfo({
153
+ connectorConfig: {
154
+ connector: "backlog",
155
+ operations: ["getAll", "getById"],
156
+ endpoints: {
157
+ getAll: "GET /issues",
158
+ getById: "GET /issues/{id}",
159
+ },
160
+ },
161
+ });
162
+ const readOnlyConnector = readOnlyModel.connectorConfig as ApiModelConnectorConfig;
163
+ const code = generateApiConnectorFunctionTS(readOnlyModel, "@myapp/shared", backlogConnector, readOnlyConnector);
164
+ expect(code).toContain("backlogIssue-get-all");
165
+ expect(code).not.toContain("backlogIssue-create");
166
+ expect(code).not.toContain("backlogIssue-update");
167
+ });
168
+ });
169
+
170
+ // ─── C# RDB Connector ──────────────────────────────────────
171
+
172
+ describe("generateRdbConnectorFunctionCSharp", () => {
173
+ const model = createRdbConnectorModelInfo();
174
+ const modelConnector = model.connectorConfig as RdbModelConnectorConfig;
175
+
176
+ it("generates MySQL C# connector functions", () => {
177
+ const code = generateRdbConnectorFunctionCSharp(model, mysqlConnector, modelConnector);
178
+ expect(code).toContain("MySqlConnection");
179
+ expect(code).toContain("MYSQL_CONNECTION_STRING");
180
+ expect(code).toContain("UserConnectorFunctions");
181
+ expect(code).toContain('[Function("userGetAll")]');
182
+ expect(code).toContain('[Function("userGetById")]');
183
+ // Should NOT contain write operations
184
+ expect(code).not.toContain('[Function("userCreate")]');
185
+ expect(code).not.toContain('[Function("userUpdate")]');
186
+ });
187
+
188
+ it("generates PostgreSQL C# connector", () => {
189
+ const code = generateRdbConnectorFunctionCSharp(model, postgresConnector, modelConnector);
190
+ expect(code).toContain("NpgsqlConnection");
191
+ expect(code).toContain("PG_CONNECTION_STRING");
192
+ });
193
+
194
+ it("generates SQL Server C# connector", () => {
195
+ const code = generateRdbConnectorFunctionCSharp(model, sqlserverConnector, modelConnector);
196
+ expect(code).toContain("SqlConnection");
197
+ expect(code).toContain("MSSQL_CONNECTION_STRING");
198
+ });
199
+
200
+ it("uses Route attribute with correct paths", () => {
201
+ const code = generateRdbConnectorFunctionCSharp(model, mysqlConnector, modelConnector);
202
+ expect(code).toContain('Route = "user"');
203
+ expect(code).toContain('Route = "user/{id}"');
204
+ });
205
+ });
206
+
207
+ // ─── C# API Connector ──────────────────────────────────────
208
+
209
+ describe("generateApiConnectorFunctionCSharp", () => {
210
+ const model = createApiConnectorModelInfo();
211
+ const modelConnector = model.connectorConfig as ApiModelConnectorConfig;
212
+
213
+ it("generates API connector with CRUD operations", () => {
214
+ const code = generateApiConnectorFunctionCSharp(model, backlogConnector, modelConnector);
215
+ expect(code).toContain("BacklogIssueConnectorFunctions");
216
+ expect(code).toContain("HttpClient");
217
+ expect(code).toContain("BACKLOG_API_BASE_URL");
218
+ expect(code).toContain('[Function("backlogIssueGetAll")]');
219
+ expect(code).toContain('[Function("backlogIssueGetById")]');
220
+ expect(code).toContain('[Function("backlogIssueCreate")]');
221
+ expect(code).toContain('[Function("backlogIssueUpdate")]');
222
+ });
223
+
224
+ it("includes auth configuration for apiKey", () => {
225
+ const code = generateApiConnectorFunctionCSharp(model, backlogConnector, modelConnector);
226
+ expect(code).toContain("BACKLOG_API_KEY");
227
+ expect(code).toContain("apiKey");
228
+ });
229
+
230
+ it("generates bearer auth for C#", () => {
231
+ const code = generateApiConnectorFunctionCSharp(model, bearerConnector, modelConnector);
232
+ expect(code).toContain("Authorization");
233
+ expect(code).toContain("Bearer");
234
+ expect(code).toContain("EXT_API_TOKEN");
235
+ });
236
+ });
237
+
238
+ // ─── Python RDB Connector ───────────────────────────────────
239
+
240
+ describe("generateRdbConnectorFunctionPython", () => {
241
+ const model = createRdbConnectorModelInfo();
242
+ const modelConnector = model.connectorConfig as RdbModelConnectorConfig;
243
+
244
+ it("generates MySQL Python connector", () => {
245
+ const result = generateRdbConnectorFunctionPython(model, mysqlConnector, modelConnector);
246
+ expect(result.registration).toContain("user");
247
+ expect(result.blueprint).toContain("mysql.connector");
248
+ expect(result.blueprint).toContain("MYSQL_CONNECTION_STRING");
249
+ expect(result.blueprint).toContain("SELECT * FROM users");
250
+ expect(result.blueprint).toContain('route="user"');
251
+ expect(result.blueprint).toContain('methods=["GET"]');
252
+ });
253
+
254
+ it("returns registration and blueprint as object", () => {
255
+ const result = generateRdbConnectorFunctionPython(model, mysqlConnector, modelConnector);
256
+ expect(result).toHaveProperty("registration");
257
+ expect(result).toHaveProperty("blueprint");
258
+ expect(typeof result.registration).toBe("string");
259
+ expect(typeof result.blueprint).toBe("string");
260
+ });
261
+ });
262
+
263
+ // ─── Python API Connector ───────────────────────────────────
264
+
265
+ describe("generateApiConnectorFunctionPython", () => {
266
+ const model = createApiConnectorModelInfo();
267
+ const modelConnector = model.connectorConfig as ApiModelConnectorConfig;
268
+
269
+ it("generates API Python connector with CRUD", () => {
270
+ const result = generateApiConnectorFunctionPython(model, backlogConnector, modelConnector);
271
+ expect(result.blueprint).toContain("BACKLOG_API_BASE_URL");
272
+ expect(result.blueprint).toContain("requests");
273
+ expect(result.blueprint).toContain('route="backlogIssue"');
274
+ });
275
+
276
+ it("includes apiKey auth in Python", () => {
277
+ const result = generateApiConnectorFunctionPython(model, backlogConnector, modelConnector);
278
+ expect(result.blueprint).toContain("BACKLOG_API_KEY");
279
+ expect(result.blueprint).toContain("apiKey");
280
+ });
281
+
282
+ it("generates bearer auth for Python", () => {
283
+ const result = generateApiConnectorFunctionPython(model, bearerConnector, modelConnector);
284
+ expect(result.blueprint).toContain("Authorization");
285
+ expect(result.blueprint).toContain("Bearer");
286
+ expect(result.blueprint).toContain("EXT_API_TOKEN");
287
+ });
288
+ });