specforge-mcp 0.2.2 → 0.4.0

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 (76) hide show
  1. package/dist/engine/skill-generator/sections-skills.d.ts +4 -0
  2. package/dist/engine/skill-generator/sections-skills.d.ts.map +1 -0
  3. package/dist/engine/skill-generator/sections-skills.js +51 -0
  4. package/dist/engine/skill-generator/sections-skills.js.map +1 -0
  5. package/dist/engine/skill-generator/sections.d.ts +1 -0
  6. package/dist/engine/skill-generator/sections.d.ts.map +1 -1
  7. package/dist/engine/skill-generator/sections.js +2 -1
  8. package/dist/engine/skill-generator/sections.js.map +1 -1
  9. package/dist/engine/skill-generator.d.ts +3 -1
  10. package/dist/engine/skill-generator.d.ts.map +1 -1
  11. package/dist/engine/skill-generator.js +11 -2
  12. package/dist/engine/skill-generator.js.map +1 -1
  13. package/dist/engine/test-plan-generator.d.ts +3 -0
  14. package/dist/engine/test-plan-generator.d.ts.map +1 -0
  15. package/dist/engine/test-plan-generator.js +166 -0
  16. package/dist/engine/test-plan-generator.js.map +1 -0
  17. package/dist/engine/test-spec-generator.d.ts +8 -0
  18. package/dist/engine/test-spec-generator.d.ts.map +1 -0
  19. package/dist/engine/test-spec-generator.js +348 -0
  20. package/dist/engine/test-spec-generator.js.map +1 -0
  21. package/dist/tools/generate-rules.d.ts.map +1 -1
  22. package/dist/tools/generate-rules.js +20 -0
  23. package/dist/tools/generate-rules.js.map +1 -1
  24. package/dist/tools/generate-tests/generators/database-test-generator.d.ts +11 -0
  25. package/dist/tools/generate-tests/generators/database-test-generator.d.ts.map +1 -0
  26. package/dist/tools/generate-tests/generators/database-test-generator.js +329 -0
  27. package/dist/tools/generate-tests/generators/database-test-generator.js.map +1 -0
  28. package/dist/tools/generate-tests/generators/graphql-test-generator.d.ts +17 -0
  29. package/dist/tools/generate-tests/generators/graphql-test-generator.d.ts.map +1 -0
  30. package/dist/tools/generate-tests/generators/graphql-test-generator.js +235 -0
  31. package/dist/tools/generate-tests/generators/graphql-test-generator.js.map +1 -0
  32. package/dist/tools/generate-tests/generators/grpc-test-generator.d.ts +17 -0
  33. package/dist/tools/generate-tests/generators/grpc-test-generator.d.ts.map +1 -0
  34. package/dist/tools/generate-tests/generators/grpc-test-generator.js +283 -0
  35. package/dist/tools/generate-tests/generators/grpc-test-generator.js.map +1 -0
  36. package/dist/tools/generate-tests/generators/microservices-test-generator.d.ts +10 -0
  37. package/dist/tools/generate-tests/generators/microservices-test-generator.d.ts.map +1 -0
  38. package/dist/tools/generate-tests/generators/microservices-test-generator.js +341 -0
  39. package/dist/tools/generate-tests/generators/microservices-test-generator.js.map +1 -0
  40. package/dist/tools/generate-tests/generators/security-test-generator.d.ts +11 -0
  41. package/dist/tools/generate-tests/generators/security-test-generator.d.ts.map +1 -0
  42. package/dist/tools/generate-tests/generators/security-test-generator.js +318 -0
  43. package/dist/tools/generate-tests/generators/security-test-generator.js.map +1 -0
  44. package/dist/tools/generate-tests/generators/visual-regression-generator.d.ts +19 -0
  45. package/dist/tools/generate-tests/generators/visual-regression-generator.d.ts.map +1 -0
  46. package/dist/tools/generate-tests/generators/visual-regression-generator.js +304 -0
  47. package/dist/tools/generate-tests/generators/visual-regression-generator.js.map +1 -0
  48. package/dist/tools/generate-tests/generators/websocket-test-generator.d.ts +17 -0
  49. package/dist/tools/generate-tests/generators/websocket-test-generator.d.ts.map +1 -0
  50. package/dist/tools/generate-tests/generators/websocket-test-generator.js +243 -0
  51. package/dist/tools/generate-tests/generators/websocket-test-generator.js.map +1 -0
  52. package/dist/tools/generate-tests/plan-mode-handler.d.ts +3 -0
  53. package/dist/tools/generate-tests/plan-mode-handler.d.ts.map +1 -0
  54. package/dist/tools/generate-tests/plan-mode-handler.js +54 -0
  55. package/dist/tools/generate-tests/plan-mode-handler.js.map +1 -0
  56. package/dist/tools/generate-tests/spec-dispatcher.d.ts.map +1 -1
  57. package/dist/tools/generate-tests/spec-dispatcher.js +46 -0
  58. package/dist/tools/generate-tests/spec-dispatcher.js.map +1 -1
  59. package/dist/tools/generate-tests/test-helpers.d.ts +8 -0
  60. package/dist/tools/generate-tests/test-helpers.d.ts.map +1 -0
  61. package/dist/tools/generate-tests/test-helpers.js +120 -0
  62. package/dist/tools/generate-tests/test-helpers.js.map +1 -0
  63. package/dist/tools/generate-tests.d.ts.map +1 -1
  64. package/dist/tools/generate-tests.js +6 -118
  65. package/dist/tools/generate-tests.js.map +1 -1
  66. package/dist/tools/init-project/handler.d.ts.map +1 -1
  67. package/dist/tools/init-project/handler.js +29 -0
  68. package/dist/tools/init-project/handler.js.map +1 -1
  69. package/dist/types/stack/recommend.d.ts +2 -0
  70. package/dist/types/stack/recommend.d.ts.map +1 -1
  71. package/dist/types/testing.d.ts +51 -0
  72. package/dist/types/testing.d.ts.map +1 -1
  73. package/package.json +3 -2
  74. package/src/i18n/messages/en.json +333 -0
  75. package/src/i18n/messages/es.json +333 -0
  76. package/src/i18n/messages/pt.json +333 -0
@@ -0,0 +1,329 @@
1
+ // tools/generate-tests/generators/database-test-generator.ts — SPEC-058b Section F
2
+ // Generates database & data layer test scaffolds:
3
+ // migrations, repository/DAO CRUD, caching, full-text search.
4
+ const CACHE_SIGNALS = ['redis', 'memcached', 'node-cache', 'caffeine', 'ehcache', 'valkey'];
5
+ const SEARCH_SIGNALS = [
6
+ 'elasticsearch',
7
+ 'meilisearch',
8
+ 'algolia',
9
+ 'opensearch',
10
+ 'solr',
11
+ 'typesense',
12
+ ];
13
+ export function detectCaching(knowledge) {
14
+ const stackLower = knowledge.stack.map((s) => s.toLowerCase());
15
+ for (const sig of CACHE_SIGNALS) {
16
+ if (stackLower.some((s) => s.includes(sig))) {
17
+ return sig;
18
+ }
19
+ }
20
+ return null;
21
+ }
22
+ export function detectSearchEngine(knowledge) {
23
+ const stackLower = knowledge.stack.map((s) => s.toLowerCase());
24
+ for (const sig of SEARCH_SIGNALS) {
25
+ if (stackLower.some((s) => s.includes(sig))) {
26
+ return sig;
27
+ }
28
+ }
29
+ return null;
30
+ }
31
+ export function hasOrm(knowledge) {
32
+ return knowledge.orm !== null && knowledge.orm !== 'unknown';
33
+ }
34
+ export function isDatabaseTestProject(knowledge) {
35
+ return knowledge.database !== 'unknown';
36
+ }
37
+ export function generateDatabaseTestDefs(title, testDir, _testExt, knowledge) {
38
+ const defs = [
39
+ {
40
+ name: `${title} — migration: schema up/down and rollback safety`,
41
+ type: 'integration',
42
+ file: `${testDir}/database/`,
43
+ description: 'Verify schema migrations run up/down cleanly and preserve data integrity',
44
+ priority: 'critical',
45
+ automatable: true,
46
+ },
47
+ {
48
+ name: `${title} — repository: CRUD operations on ${knowledge.database}`,
49
+ type: 'integration',
50
+ file: `${testDir}/database/`,
51
+ description: 'Verify create, read, update, delete operations on the data layer',
52
+ priority: 'high',
53
+ automatable: true,
54
+ },
55
+ ];
56
+ if (hasOrm(knowledge)) {
57
+ defs.push({
58
+ name: `${title} — transactions and optimistic locking`,
59
+ type: 'integration',
60
+ file: `${testDir}/database/`,
61
+ description: 'Verify transaction commit/rollback and optimistic locking conflict detection',
62
+ priority: 'high',
63
+ automatable: true,
64
+ });
65
+ }
66
+ const cache = detectCaching(knowledge);
67
+ if (cache !== null) {
68
+ defs.push({
69
+ name: `${title} — caching (${cache}): hit/miss, TTL, invalidation`,
70
+ type: 'integration',
71
+ file: `${testDir}/database/`,
72
+ description: 'Verify cache hit/miss behavior, TTL expiration, and invalidation patterns',
73
+ priority: 'high',
74
+ automatable: true,
75
+ });
76
+ }
77
+ const search = detectSearchEngine(knowledge);
78
+ if (search !== null) {
79
+ defs.push({
80
+ name: `${title} — full-text search (${search}): indexing and query relevance`,
81
+ type: 'integration',
82
+ file: `${testDir}/database/`,
83
+ description: 'Verify document indexing, query relevance ranking, and fuzzy matching',
84
+ priority: 'medium',
85
+ automatable: true,
86
+ });
87
+ }
88
+ return defs;
89
+ }
90
+ // ---------------------------------------------------------------------------
91
+ // Scaffold builders
92
+ // ---------------------------------------------------------------------------
93
+ function buildTsDatabaseTest(title, db, orm, cache, search) {
94
+ const cacheSection = cache
95
+ ? `
96
+ describe('Caching (${cache})', () => {
97
+ it('returns cached value on cache hit', async () => {
98
+ // await cache.set('user:1', { name: 'Test' }, { ttl: 60 });
99
+ // const result = await cache.get('user:1');
100
+ // expect(result).toMatchObject({ name: 'Test' });
101
+ expect(true).toBe(true);
102
+ });
103
+
104
+ it('returns null on cache miss', async () => {
105
+ expect(true).toBe(true);
106
+ });
107
+
108
+ it('expires entry after TTL', async () => {
109
+ expect(true).toBe(true);
110
+ });
111
+
112
+ it('invalidates cache on data update', async () => {
113
+ expect(true).toBe(true);
114
+ });
115
+ });
116
+ `
117
+ : '';
118
+ const searchSection = search
119
+ ? `
120
+ describe('Full-text search (${search})', () => {
121
+ it('indexes document and makes it searchable', async () => {
122
+ // await searchClient.index('products').addDocuments([{ id: 1, name: 'Widget' }]);
123
+ // const results = await searchClient.index('products').search('Widget');
124
+ // expect(results.hits).toHaveLength(1);
125
+ expect(true).toBe(true);
126
+ });
127
+
128
+ it('returns relevant results ranked by score', async () => {
129
+ expect(true).toBe(true);
130
+ });
131
+
132
+ it('supports fuzzy matching for typos', async () => {
133
+ expect(true).toBe(true);
134
+ });
135
+ });
136
+ `
137
+ : '';
138
+ const ormSection = orm
139
+ ? `
140
+ describe('Transactions and locking', () => {
141
+ it('commits transaction on success', async () => {
142
+ expect(true).toBe(true);
143
+ });
144
+
145
+ it('rolls back transaction on error', async () => {
146
+ expect(true).toBe(true);
147
+ });
148
+
149
+ it('detects optimistic locking conflict', async () => {
150
+ // Concurrent update with stale version → should throw
151
+ expect(true).toBe(true);
152
+ });
153
+ });
154
+ `
155
+ : '';
156
+ return `// Database & Data Layer tests for: ${title}
157
+ // Generated by SpecForge SDD MCP Server (SPEC-058b)
158
+ import { describe, it, expect } from 'vitest';
159
+
160
+ describe('${title} — Database Tests (${db})', () => {
161
+ describe('Migrations', () => {
162
+ it('schema migration up applies cleanly', async () => {
163
+ // await migrate.up();
164
+ // const tables = await db.query("SELECT name FROM sqlite_master WHERE type='table'");
165
+ // expect(tables).toContainEqual({ name: 'users' });
166
+ expect(true).toBe(true); // Replace with real migration assertion
167
+ });
168
+
169
+ it('schema migration down reverts cleanly', async () => {
170
+ expect(true).toBe(true);
171
+ });
172
+
173
+ it('data integrity preserved after migration', async () => {
174
+ expect(true).toBe(true);
175
+ });
176
+
177
+ it('rollback does not lose existing data', async () => {
178
+ expect(true).toBe(true);
179
+ });
180
+ });
181
+
182
+ describe('Repository / DAO — CRUD', () => {
183
+ it('creates a record and returns its id', async () => {
184
+ // const user = await repo.create({ name: 'Test', email: 'test@example.com' });
185
+ // expect(user.id).toBeDefined();
186
+ expect(true).toBe(true);
187
+ });
188
+
189
+ it('reads a record by id', async () => {
190
+ expect(true).toBe(true);
191
+ });
192
+
193
+ it('updates a record', async () => {
194
+ expect(true).toBe(true);
195
+ });
196
+
197
+ it('deletes a record', async () => {
198
+ expect(true).toBe(true);
199
+ });
200
+
201
+ it('returns null for non-existent record', async () => {
202
+ expect(true).toBe(true);
203
+ });
204
+ });
205
+ ${ormSection}${cacheSection}${searchSection}});
206
+ `;
207
+ }
208
+ function buildPythonDatabaseTest(title, db, orm, cache, search) {
209
+ const cls = title.replace(/[^a-zA-Z0-9]/g, '');
210
+ const ormSection = orm
211
+ ? `
212
+ def test_transaction_commits_on_success(self) -> None:
213
+ """Transaction commits all changes on success."""
214
+ pass
215
+
216
+ def test_transaction_rolls_back_on_error(self) -> None:
217
+ """Transaction rolls back on exception."""
218
+ pass
219
+
220
+ def test_optimistic_locking_conflict(self) -> None:
221
+ """Concurrent update with stale version raises conflict."""
222
+ pass
223
+ `
224
+ : '';
225
+ const cacheSection = cache
226
+ ? `
227
+ def test_cache_hit_returns_value(self) -> None:
228
+ """Cache returns stored value on hit."""
229
+ pass
230
+
231
+ def test_cache_miss_returns_none(self) -> None:
232
+ """Cache returns None on miss."""
233
+ pass
234
+
235
+ def test_cache_ttl_expiration(self) -> None:
236
+ """Cache entry expires after TTL."""
237
+ pass
238
+
239
+ def test_cache_invalidation_on_update(self) -> None:
240
+ """Cache is invalidated when underlying data changes."""
241
+ pass
242
+ `
243
+ : '';
244
+ const searchSection = search
245
+ ? `
246
+ def test_index_document_searchable(self) -> None:
247
+ """Indexed document is returned by search query."""
248
+ pass
249
+
250
+ def test_query_relevance_ranking(self) -> None:
251
+ """Results are ranked by relevance score."""
252
+ pass
253
+
254
+ def test_fuzzy_matching_for_typos(self) -> None:
255
+ """Fuzzy search finds results despite typos."""
256
+ pass
257
+ `
258
+ : '';
259
+ return `"""Database & Data Layer tests for: ${title}
260
+ Generated by SpecForge SDD MCP Server (SPEC-058b).
261
+ Database: ${db}
262
+ """
263
+ import pytest
264
+
265
+
266
+ class Test${cls}Database:
267
+ """Database test suite for ${title} (${db})."""
268
+
269
+ def test_migration_up_applies_cleanly(self) -> None:
270
+ """Schema migration up creates expected tables."""
271
+ # TODO: result = alembic.command.upgrade(config, 'head')
272
+ pass
273
+
274
+ def test_migration_down_reverts_cleanly(self) -> None:
275
+ """Schema migration down removes tables cleanly."""
276
+ pass
277
+
278
+ def test_data_integrity_after_migration(self) -> None:
279
+ """Existing data is preserved after migration."""
280
+ pass
281
+
282
+ def test_rollback_safety(self) -> None:
283
+ """Rollback does not lose existing data."""
284
+ pass
285
+
286
+ def test_create_record(self) -> None:
287
+ """Creates a record and returns its id."""
288
+ # TODO: user = repo.create(name='Test', email='test@example.com')
289
+ # assert user.id is not None
290
+ pass
291
+
292
+ def test_read_record_by_id(self) -> None:
293
+ """Reads a record by primary key."""
294
+ pass
295
+
296
+ def test_update_record(self) -> None:
297
+ """Updates a record field."""
298
+ pass
299
+
300
+ def test_delete_record(self) -> None:
301
+ """Deletes a record."""
302
+ pass
303
+
304
+ def test_read_nonexistent_returns_none(self) -> None:
305
+ """Reading a non-existent record returns None."""
306
+ pass
307
+ ${ormSection}${cacheSection}${searchSection}`;
308
+ }
309
+ export function generateDatabaseTestFiles(spec, testDir, framework, language, testExt, autoGenerate, knowledge) {
310
+ const cache = detectCaching(knowledge);
311
+ const search = detectSearchEngine(knowledge);
312
+ const orm = hasOrm(knowledge);
313
+ const db = knowledge.database;
314
+ const isPython = language === 'python';
315
+ const ext = isPython ? 'py' : testExt;
316
+ const fw = isPython ? 'pytest' : framework;
317
+ const content = isPython
318
+ ? buildPythonDatabaseTest(spec.title, db, orm, cache, search)
319
+ : buildTsDatabaseTest(spec.title, db, orm, cache, search);
320
+ return [
321
+ {
322
+ path: `${testDir}/database/${spec.slug}.database.test.${ext}`,
323
+ framework: fw,
324
+ content,
325
+ ready: autoGenerate,
326
+ },
327
+ ];
328
+ }
329
+ //# sourceMappingURL=database-test-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-test-generator.js","sourceRoot":"","sources":["../../../../src/tools/generate-tests/generators/database-test-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { TestDefinition, TestFile, ProjectKnowledge } from '../../../types/index.js';
2
+ /**
3
+ * Returns true when the project uses GraphQL (Apollo, Urql, Relay, Hasura, etc.).
4
+ */
5
+ export declare function isGraphqlProject(knowledge: ProjectKnowledge): boolean;
6
+ /**
7
+ * Generate GraphQL test definitions covering schema, queries, mutations, and security.
8
+ */
9
+ export declare function generateGraphqlTestDefs(title: string, testDir: string, _testExt: string): TestDefinition[];
10
+ /**
11
+ * Generate GraphQL test file scaffolds.
12
+ */
13
+ export declare function generateGraphqlTestFiles(spec: {
14
+ title: string;
15
+ slug: string;
16
+ }, testDir: string, framework: string, language: string, testExt: string, autoGenerate: boolean): TestFile[];
17
+ //# sourceMappingURL=graphql-test-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql-test-generator.d.ts","sourceRoot":"","sources":["../../../../src/tools/generate-tests/generators/graphql-test-generator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAI1F;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,gBAAgB,GAAG,OAAO,CAKrE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,cAAc,EAAE,CA2DlB;AAmJD;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACrC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,OAAO,GACpB,QAAQ,EAAE,CAcZ"}
@@ -0,0 +1,235 @@
1
+ // tools/generate-tests/generators/graphql-test-generator.ts — SPEC-058a Section D
2
+ // Generates GraphQL API test scaffolds: schema validation, queries, mutations, security.
3
+ const GRAPHQL_SIGNALS = ['graphql', 'apollo', 'urql', 'relay', 'hasura', '@graphql-codegen'];
4
+ /**
5
+ * Returns true when the project uses GraphQL (Apollo, Urql, Relay, Hasura, etc.).
6
+ */
7
+ export function isGraphqlProject(knowledge) {
8
+ const stackLower = knowledge.stack.map((s) => s.toLowerCase());
9
+ const hasSignal = GRAPHQL_SIGNALS.some((sig) => stackLower.some((s) => s.includes(sig)));
10
+ const hasContract = knowledge.apiContracts.some((c) => c.type.toLowerCase() === 'graphql');
11
+ return hasSignal || hasContract;
12
+ }
13
+ /**
14
+ * Generate GraphQL test definitions covering schema, queries, mutations, and security.
15
+ */
16
+ export function generateGraphqlTestDefs(title, testDir, _testExt) {
17
+ return [
18
+ {
19
+ name: `${title} — GraphQL: schema validation passes introspection`,
20
+ type: 'integration',
21
+ file: `${testDir}/graphql/`,
22
+ description: 'Verify the GraphQL schema is valid and all types resolve correctly',
23
+ priority: 'critical',
24
+ automatable: true,
25
+ },
26
+ {
27
+ name: `${title} — GraphQL: query with variables returns expected data shape`,
28
+ type: 'integration',
29
+ file: `${testDir}/graphql/`,
30
+ description: 'Execute a parameterized query and assert the response matches the schema',
31
+ priority: 'high',
32
+ automatable: true,
33
+ },
34
+ {
35
+ name: `${title} — GraphQL: mutation creates resource and returns it`,
36
+ type: 'integration',
37
+ file: `${testDir}/graphql/`,
38
+ description: 'Execute a create mutation and verify the returned payload and side-effects',
39
+ priority: 'high',
40
+ automatable: true,
41
+ },
42
+ {
43
+ name: `${title} — GraphQL: introspection disabled in production`,
44
+ type: 'integration',
45
+ file: `${testDir}/graphql/`,
46
+ description: 'Verify introspection query returns an error when NODE_ENV=production',
47
+ priority: 'critical',
48
+ automatable: true,
49
+ },
50
+ {
51
+ name: `${title} — GraphQL: N+1 query detection on nested resolvers`,
52
+ type: 'integration',
53
+ file: `${testDir}/graphql/`,
54
+ description: 'Assert that nested list queries do not trigger N+1 database calls',
55
+ priority: 'high',
56
+ automatable: true,
57
+ },
58
+ {
59
+ name: `${title} — GraphQL: query depth limiting rejects deep queries`,
60
+ type: 'integration',
61
+ file: `${testDir}/graphql/`,
62
+ description: 'Verify that queries exceeding the configured depth limit are rejected',
63
+ priority: 'medium',
64
+ automatable: true,
65
+ },
66
+ {
67
+ name: `${title} — GraphQL: auth resolver rejects unauthenticated requests`,
68
+ type: 'integration',
69
+ file: `${testDir}/graphql/`,
70
+ description: 'Verify that protected resolvers return UNAUTHENTICATED error without a token',
71
+ priority: 'critical',
72
+ automatable: true,
73
+ },
74
+ ];
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // Content generators
78
+ // ---------------------------------------------------------------------------
79
+ function buildTsGraphqlTest(title) {
80
+ return `// GraphQL API tests for: ${title}
81
+ // Generated by SpecForge SDD MCP Server (SPEC-058a)
82
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
83
+ // Adjust import path to match your server setup
84
+ // import { createTestServer } from '../helpers/test-server';
85
+ // import { request } from 'graphql-request';
86
+
87
+ describe('${title} — GraphQL API Tests', () => {
88
+ // let server: ReturnType<typeof createTestServer>;
89
+ // let endpoint: string;
90
+
91
+ beforeAll(async () => {
92
+ // server = await createTestServer();
93
+ // endpoint = \`http://localhost:\${server.port}/graphql\`;
94
+ });
95
+
96
+ afterAll(async () => {
97
+ // await server.close();
98
+ });
99
+
100
+ describe('Schema validation', () => {
101
+ it('schema passes introspection query', async () => {
102
+ // const result = await request(endpoint, \`{ __schema { types { name } } }\`);
103
+ // expect(result.__schema.types.length).toBeGreaterThan(0);
104
+ expect(true).toBe(true); // Replace with real introspection assertion
105
+ });
106
+ });
107
+
108
+ describe('Query with variables', () => {
109
+ it('returns expected data shape for parameterized query', async () => {
110
+ // const query = \`
111
+ // query GetItem($id: ID!) {
112
+ // item(id: $id) { id name createdAt }
113
+ // }
114
+ // \`;
115
+ // const result = await request(endpoint, query, { id: 'test-id' });
116
+ // expect(result.item).toMatchObject({ id: 'test-id' });
117
+ expect(true).toBe(true); // Replace with real query assertion
118
+ });
119
+ });
120
+
121
+ describe('Mutation', () => {
122
+ it('creates resource and returns it', async () => {
123
+ // const mutation = \`
124
+ // mutation CreateItem($input: CreateItemInput!) {
125
+ // createItem(input: $input) { id name }
126
+ // }
127
+ // \`;
128
+ // const result = await request(endpoint, mutation, { input: { name: 'Test' } });
129
+ // expect(result.createItem.name).toBe('Test');
130
+ expect(true).toBe(true); // Replace with real mutation assertion
131
+ });
132
+ });
133
+
134
+ describe('Security', () => {
135
+ it('rejects introspection in production mode', async () => {
136
+ // const originalEnv = process.env['NODE_ENV'];
137
+ // process.env['NODE_ENV'] = 'production';
138
+ // try {
139
+ // await expect(request(endpoint, \`{ __schema { types { name } } }\`)).rejects.toThrow();
140
+ // } finally {
141
+ // process.env['NODE_ENV'] = originalEnv;
142
+ // }
143
+ expect(true).toBe(true); // Replace with production introspection test
144
+ });
145
+
146
+ it('rejects queries exceeding depth limit', async () => {
147
+ // const deepQuery = \`{ a { b { c { d { e { f { name } } } } } } }\`;
148
+ // await expect(request(endpoint, deepQuery)).rejects.toThrow();
149
+ expect(true).toBe(true); // Replace with depth-limit assertion
150
+ });
151
+
152
+ it('returns UNAUTHENTICATED error for protected resolver without token', async () => {
153
+ // const result = await request(endpoint, \`{ protectedData { secret } }\`);
154
+ // expect(result.errors?.[0]?.extensions?.code).toBe('UNAUTHENTICATED');
155
+ expect(true).toBe(true); // Replace with auth assertion
156
+ });
157
+ });
158
+ });
159
+ `;
160
+ }
161
+ function buildPythonGraphqlTest(title) {
162
+ const cls = title.replace(/\s+/g, '');
163
+ return `"""GraphQL API tests for: ${title}
164
+ Generated by SpecForge SDD MCP Server (SPEC-058a).
165
+ Supports strawberry-graphql and graphene.
166
+ """
167
+ import pytest
168
+ # from strawberry.test import Client # strawberry
169
+ # from graphene_django.utils.testing import GraphQLTestCase # graphene-django
170
+
171
+
172
+ class Test${cls}GraphQL:
173
+ """GraphQL API test suite for ${title}."""
174
+
175
+ def test_schema_passes_introspection(self) -> None:
176
+ """Schema is valid and all types are resolvable via introspection."""
177
+ # TODO: client = Client(schema)
178
+ # result = client.execute("{ __schema { types { name } } }")
179
+ # assert "errors" not in result
180
+ pass
181
+
182
+ def test_query_with_variables_returns_expected_shape(self) -> None:
183
+ """Parameterized query returns correct data shape."""
184
+ # TODO: result = client.execute(
185
+ # "query GetItem($id: ID!) { item(id: $id) { id name } }",
186
+ # variable_values={"id": "test-id"}
187
+ # )
188
+ # assert result["data"]["item"]["id"] == "test-id"
189
+ pass
190
+
191
+ def test_mutation_creates_resource(self) -> None:
192
+ """Create mutation returns the newly created resource."""
193
+ # TODO: result = client.execute(
194
+ # 'mutation { createItem(name: "Test") { id name } }'
195
+ # )
196
+ # assert result["data"]["createItem"]["name"] == "Test"
197
+ pass
198
+
199
+ def test_introspection_disabled_in_production(self) -> None:
200
+ """Introspection returns an error when running in production mode."""
201
+ # TODO: assert introspection_disabled_for_production()
202
+ pass
203
+
204
+ def test_deep_query_rejected_by_depth_limiter(self) -> None:
205
+ """Queries exceeding the depth limit are rejected with an error."""
206
+ # TODO: deep_query = "{ a { b { c { d { e { f { name } } } } } } }"
207
+ # result = client.execute(deep_query)
208
+ # assert "errors" in result
209
+ pass
210
+
211
+ def test_protected_resolver_requires_auth(self) -> None:
212
+ """Protected resolver returns UNAUTHENTICATED error without token."""
213
+ # TODO: result = unauthenticated_client.execute("{ protectedData { secret } }")
214
+ # assert result["errors"][0]["extensions"]["code"] == "UNAUTHENTICATED"
215
+ pass
216
+ `;
217
+ }
218
+ /**
219
+ * Generate GraphQL test file scaffolds.
220
+ */
221
+ export function generateGraphqlTestFiles(spec, testDir, framework, language, testExt, autoGenerate) {
222
+ const isPython = language === 'python';
223
+ const resolvedExt = isPython ? 'py' : testExt;
224
+ const resolvedFramework = isPython ? 'pytest' : framework;
225
+ const content = isPython ? buildPythonGraphqlTest(spec.title) : buildTsGraphqlTest(spec.title);
226
+ return [
227
+ {
228
+ path: `${testDir}/graphql/${spec.slug}.graphql.test.${resolvedExt}`,
229
+ framework: resolvedFramework,
230
+ content,
231
+ ready: autoGenerate,
232
+ },
233
+ ];
234
+ }
235
+ //# sourceMappingURL=graphql-test-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql-test-generator.js","sourceRoot":"","sources":["../../../../src/tools/generate-tests/generators/graphql-test-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { TestDefinition, TestFile, ProjectKnowledge } from '../../../types/index.js';
2
+ /**
3
+ * Returns true when the project uses gRPC / Protocol Buffers.
4
+ */
5
+ export declare function isGrpcProject(knowledge: ProjectKnowledge): boolean;
6
+ /**
7
+ * Generate gRPC test definitions: proto contracts, unary, streaming, deadlines.
8
+ */
9
+ export declare function generateGrpcTestDefs(title: string, testDir: string, _testExt: string): TestDefinition[];
10
+ /**
11
+ * Generate gRPC test file scaffolds for the target language.
12
+ */
13
+ export declare function generateGrpcTestFiles(spec: {
14
+ title: string;
15
+ slug: string;
16
+ }, testDir: string, framework: string, language: string, testExt: string, autoGenerate: boolean): TestFile[];
17
+ //# sourceMappingURL=grpc-test-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grpc-test-generator.d.ts","sourceRoot":"","sources":["../../../../src/tools/generate-tests/generators/grpc-test-generator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAI1F;;GAEG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,gBAAgB,GAAG,OAAO,CAMlE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,cAAc,EAAE,CAmDlB;AA6LD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACrC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,OAAO,GACpB,QAAQ,EAAE,CA2BZ"}