@aicgen/aicgen 1.0.0-beta.1
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/.claude/agents/architecture-reviewer.md +88 -0
- package/.claude/agents/guideline-checker.md +73 -0
- package/.claude/agents/security-auditor.md +108 -0
- package/.claude/guidelines/api-design.md +645 -0
- package/.claude/guidelines/architecture.md +2503 -0
- package/.claude/guidelines/best-practices.md +618 -0
- package/.claude/guidelines/code-style.md +304 -0
- package/.claude/guidelines/design-patterns.md +573 -0
- package/.claude/guidelines/devops.md +226 -0
- package/.claude/guidelines/error-handling.md +413 -0
- package/.claude/guidelines/language.md +782 -0
- package/.claude/guidelines/performance.md +706 -0
- package/.claude/guidelines/security.md +583 -0
- package/.claude/guidelines/testing.md +568 -0
- package/.claude/settings.json +98 -0
- package/.claude/settings.local.json +8 -0
- package/.env.example +23 -0
- package/.eslintrc.json +28 -0
- package/.github/workflows/release.yml +180 -0
- package/.github/workflows/test.yml +81 -0
- package/.gitmodules +3 -0
- package/.vs/ProjectSettings.json +3 -0
- package/.vs/VSWorkspaceState.json +16 -0
- package/.vs/aicgen.slnx/FileContentIndex/5f0ce2a3-fd68-4863-9e23-e428cf1794e3.vsidx +0 -0
- package/.vs/aicgen.slnx/v18/.wsuo +0 -0
- package/.vs/aicgen.slnx/v18/DocumentLayout.json +54 -0
- package/.vs/slnx.sqlite +0 -0
- package/AGENTS.md +121 -0
- package/CLAUDE.md +36 -0
- package/CONTRIBUTING.md +821 -0
- package/LICENSE +21 -0
- package/README.md +199 -0
- package/assets/icon.svg +34 -0
- package/assets/logo.svg +41 -0
- package/bun.lock +848 -0
- package/data/LICENSE +21 -0
- package/data/README.md +203 -0
- package/data/api/basics.md +292 -0
- package/data/api/index.md +8 -0
- package/data/api/pagination.md +142 -0
- package/data/api/rest.md +137 -0
- package/data/api/versioning.md +60 -0
- package/data/architecture/clean-architecture/index.md +7 -0
- package/data/architecture/clean-architecture/layers.md +111 -0
- package/data/architecture/ddd/index.md +8 -0
- package/data/architecture/ddd/strategic.md +89 -0
- package/data/architecture/ddd/tactical.md +132 -0
- package/data/architecture/event-driven/index.md +7 -0
- package/data/architecture/event-driven/messaging.md +242 -0
- package/data/architecture/event-driven/patterns.md +129 -0
- package/data/architecture/feature-toggles/index.md +7 -0
- package/data/architecture/feature-toggles/patterns.md +73 -0
- package/data/architecture/gui/index.md +7 -0
- package/data/architecture/gui/patterns.md +132 -0
- package/data/architecture/hexagonal/ports-adapters.md +132 -0
- package/data/architecture/index.md +12 -0
- package/data/architecture/layered/index.md +7 -0
- package/data/architecture/layered/layers.md +100 -0
- package/data/architecture/microservices/api-gateway.md +56 -0
- package/data/architecture/microservices/boundaries.md +80 -0
- package/data/architecture/microservices/communication.md +97 -0
- package/data/architecture/microservices/data.md +92 -0
- package/data/architecture/microservices/index.md +11 -0
- package/data/architecture/microservices/resilience.md +111 -0
- package/data/architecture/modular-monolith/boundaries.md +133 -0
- package/data/architecture/modular-monolith/structure.md +131 -0
- package/data/architecture/serverless/best-practices.md +322 -0
- package/data/architecture/serverless/index.md +7 -0
- package/data/architecture/serverless/patterns.md +80 -0
- package/data/architecture/solid/index.md +7 -0
- package/data/architecture/solid/principles.md +187 -0
- package/data/database/basics.md +365 -0
- package/data/database/design-patterns.md +68 -0
- package/data/database/index.md +8 -0
- package/data/database/indexing.md +136 -0
- package/data/database/nosql.md +223 -0
- package/data/database/schema.md +137 -0
- package/data/devops/ci-cd.md +66 -0
- package/data/devops/index.md +8 -0
- package/data/devops/observability.md +73 -0
- package/data/devops/practices.md +77 -0
- package/data/error-handling/basics.md +222 -0
- package/data/error-handling/index.md +7 -0
- package/data/error-handling/strategy.md +185 -0
- package/data/guideline-mappings.yml +1077 -0
- package/data/index.md +3 -0
- package/data/language/csharp/basics.md +210 -0
- package/data/language/csharp/testing.md +252 -0
- package/data/language/go/basics.md +158 -0
- package/data/language/go/testing.md +192 -0
- package/data/language/index.md +14 -0
- package/data/language/java/basics.md +184 -0
- package/data/language/java/testing.md +273 -0
- package/data/language/javascript/basics.md +217 -0
- package/data/language/javascript/testing.md +269 -0
- package/data/language/python/async.md +100 -0
- package/data/language/python/basics.md +100 -0
- package/data/language/python/index.md +10 -0
- package/data/language/python/testing.md +125 -0
- package/data/language/python/types.md +99 -0
- package/data/language/ruby/basics.md +227 -0
- package/data/language/ruby/testing.md +267 -0
- package/data/language/rust/basics.md +175 -0
- package/data/language/rust/testing.md +219 -0
- package/data/language/typescript/async.md +103 -0
- package/data/language/typescript/basics.md +87 -0
- package/data/language/typescript/config.md +95 -0
- package/data/language/typescript/error-handling.md +98 -0
- package/data/language/typescript/generics.md +85 -0
- package/data/language/typescript/index.md +14 -0
- package/data/language/typescript/interfaces-types.md +83 -0
- package/data/language/typescript/performance.md +103 -0
- package/data/language/typescript/testing.md +98 -0
- package/data/patterns/base-patterns.md +105 -0
- package/data/patterns/concurrency.md +87 -0
- package/data/patterns/data-access.md +83 -0
- package/data/patterns/distribution.md +86 -0
- package/data/patterns/domain-logic.md +81 -0
- package/data/patterns/gof.md +109 -0
- package/data/patterns/index.md +12 -0
- package/data/performance/async.md +148 -0
- package/data/performance/basics.md +324 -0
- package/data/performance/caching-strategies.md +68 -0
- package/data/performance/caching.md +152 -0
- package/data/performance/index.md +8 -0
- package/data/practices/code-review.md +52 -0
- package/data/practices/documentation.md +260 -0
- package/data/practices/index.md +11 -0
- package/data/practices/planning.md +142 -0
- package/data/practices/refactoring.md +91 -0
- package/data/practices/version-control.md +55 -0
- package/data/security/auth-jwt.md +159 -0
- package/data/security/headers.md +143 -0
- package/data/security/index.md +10 -0
- package/data/security/injection.md +119 -0
- package/data/security/secrets.md +148 -0
- package/data/style/index.md +8 -0
- package/data/style/naming.md +136 -0
- package/data/style/organization.md +162 -0
- package/data/templates/agents/architecture-reviewer.md +88 -0
- package/data/templates/agents/guideline-checker.md +73 -0
- package/data/templates/agents/security-auditor.md +108 -0
- package/data/templates/antigravity/rules/architecture.md.hbs +5 -0
- package/data/templates/antigravity/rules/code-style.md.hbs +5 -0
- package/data/templates/antigravity/rules/language.md.hbs +5 -0
- package/data/templates/antigravity/rules/performance.md.hbs +5 -0
- package/data/templates/antigravity/rules/security.md.hbs +5 -0
- package/data/templates/antigravity/rules/testing.md.hbs +5 -0
- package/data/templates/antigravity/workflows/add-documentation.md.hbs +23 -0
- package/data/templates/antigravity/workflows/generate-integration-tests.md.hbs +17 -0
- package/data/templates/antigravity/workflows/generate-unit-tests.md.hbs +20 -0
- package/data/templates/antigravity/workflows/performance-audit.md.hbs +24 -0
- package/data/templates/antigravity/workflows/refactor-extract-module.md.hbs +17 -0
- package/data/templates/antigravity/workflows/security-audit.md.hbs +20 -0
- package/data/templates/hooks/formatting.json +26 -0
- package/data/templates/hooks/security.json +35 -0
- package/data/templates/hooks/testing.json +17 -0
- package/data/testing/basics.md +151 -0
- package/data/testing/index.md +9 -0
- package/data/testing/integration.md +159 -0
- package/data/testing/unit-fundamentals.md +128 -0
- package/data/testing/unit-mocking.md +116 -0
- package/data/version.json +49 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +46 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/config/profiles.d.ts +4 -0
- package/dist/config/profiles.d.ts.map +1 -0
- package/dist/config/profiles.js +30 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/settings.d.ts +7 -0
- package/dist/config/settings.d.ts.map +1 -0
- package/dist/config/settings.js +7 -0
- package/dist/config/settings.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58489 -0
- package/dist/index.js.map +1 -0
- package/dist/models/guideline.d.ts +15 -0
- package/dist/models/guideline.d.ts.map +1 -0
- package/dist/models/guideline.js +2 -0
- package/dist/models/guideline.js.map +1 -0
- package/dist/models/preference.d.ts +9 -0
- package/dist/models/preference.d.ts.map +1 -0
- package/dist/models/preference.js +2 -0
- package/dist/models/preference.js.map +1 -0
- package/dist/models/profile.d.ts +9 -0
- package/dist/models/profile.d.ts.map +1 -0
- package/dist/models/profile.js +2 -0
- package/dist/models/profile.js.map +1 -0
- package/dist/models/project.d.ts +13 -0
- package/dist/models/project.d.ts.map +1 -0
- package/dist/models/project.js +2 -0
- package/dist/models/project.js.map +1 -0
- package/dist/services/ai/anthropic.d.ts +7 -0
- package/dist/services/ai/anthropic.d.ts.map +1 -0
- package/dist/services/ai/anthropic.js +39 -0
- package/dist/services/ai/anthropic.js.map +1 -0
- package/dist/services/generator.d.ts +2 -0
- package/dist/services/generator.d.ts.map +1 -0
- package/dist/services/generator.js +4 -0
- package/dist/services/generator.js.map +1 -0
- package/dist/services/learner.d.ts +2 -0
- package/dist/services/learner.d.ts.map +1 -0
- package/dist/services/learner.js +4 -0
- package/dist/services/learner.js.map +1 -0
- package/dist/services/scanner.d.ts +3 -0
- package/dist/services/scanner.d.ts.map +1 -0
- package/dist/services/scanner.js +54 -0
- package/dist/services/scanner.js.map +1 -0
- package/dist/utils/errors.d.ts +15 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +27 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/file.d.ts +7 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/file.js +32 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +17 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/path.d.ts +6 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +14 -0
- package/dist/utils/path.js.map +1 -0
- package/docs/planning/memory-lane.md +83 -0
- package/package.json +64 -0
- package/packaging/linux/aicgen.spec +23 -0
- package/packaging/linux/control +9 -0
- package/packaging/macos/scripts/postinstall +12 -0
- package/packaging/windows/setup.nsi +92 -0
- package/planning/BRANDING-SUMMARY.md +194 -0
- package/planning/BRANDING.md +174 -0
- package/planning/BUILD.md +186 -0
- package/planning/CHUNK-IMPLEMENTATION-PLAN.md +87 -0
- package/planning/CHUNK-TAXONOMY.md +375 -0
- package/planning/CHUNKS-COMPLETE.md +382 -0
- package/planning/DESIGN.md +313 -0
- package/planning/DYNAMIC-GUIDELINES-DESIGN.md +265 -0
- package/planning/ENTERPRISE-UX-COMPLETE.md +281 -0
- package/planning/IMPLEMENTATION-PLAN.md +20 -0
- package/planning/PHASE1-COMPLETE.md +211 -0
- package/planning/PHASE2-COMPLETE.md +350 -0
- package/planning/PHASE3-COMPLETE.md +399 -0
- package/planning/PHASE4-COMPLETE.md +361 -0
- package/planning/PHASE4.5-CHUNKS.md +462 -0
- package/planning/STRUCTURE.md +170 -0
- package/scripts/add-categories.ts +87 -0
- package/scripts/build-binary.ts +46 -0
- package/scripts/embed-data.ts +105 -0
- package/scripts/generate-version.ts +150 -0
- package/scripts/test-decompress.ts +27 -0
- package/scripts/test-extract.ts +31 -0
- package/src/__tests__/services/assistant-file-writer.test.ts +400 -0
- package/src/__tests__/services/guideline-loader.test.ts +281 -0
- package/src/__tests__/services/tarball-extraction.test.ts +125 -0
- package/src/commands/add-guideline.ts +296 -0
- package/src/commands/clear.ts +61 -0
- package/src/commands/guideline-selector.ts +123 -0
- package/src/commands/init.ts +645 -0
- package/src/commands/quick-add.ts +586 -0
- package/src/commands/remove-guideline.ts +152 -0
- package/src/commands/stats.ts +49 -0
- package/src/commands/update.ts +240 -0
- package/src/config.ts +82 -0
- package/src/embedded-data.ts +1492 -0
- package/src/index.ts +67 -0
- package/src/models/profile.ts +24 -0
- package/src/models/project.ts +43 -0
- package/src/services/assistant-file-writer.ts +612 -0
- package/src/services/config-generator.ts +150 -0
- package/src/services/config-manager.ts +70 -0
- package/src/services/data-source.ts +248 -0
- package/src/services/first-run-init.ts +148 -0
- package/src/services/guideline-loader.ts +311 -0
- package/src/services/hook-generator.ts +178 -0
- package/src/services/subagent-generator.ts +310 -0
- package/src/utils/banner.ts +66 -0
- package/src/utils/errors.ts +27 -0
- package/src/utils/file.ts +67 -0
- package/src/utils/formatting.ts +172 -0
- package/src/utils/logger.ts +89 -0
- package/src/utils/path.ts +17 -0
- package/src/utils/wizard-state.ts +132 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# JavaScript Fundamentals
|
|
2
|
+
|
|
3
|
+
## Project Structure
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
myproject/
|
|
7
|
+
├── src/
|
|
8
|
+
│ ├── index.js # Entry point
|
|
9
|
+
│ ├── config/
|
|
10
|
+
│ ├── controllers/
|
|
11
|
+
│ ├── services/
|
|
12
|
+
│ ├── models/
|
|
13
|
+
│ └── utils/
|
|
14
|
+
├── tests/
|
|
15
|
+
├── package.json
|
|
16
|
+
└── .eslintrc.js
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Modern JavaScript (ES6+)
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
// const/let instead of var
|
|
23
|
+
const immutable = "cannot change";
|
|
24
|
+
let mutable = "can change";
|
|
25
|
+
|
|
26
|
+
// Arrow functions
|
|
27
|
+
const add = (a, b) => a + b;
|
|
28
|
+
const users = data.map(d => new User(d));
|
|
29
|
+
|
|
30
|
+
// Destructuring
|
|
31
|
+
const { name, email } = user;
|
|
32
|
+
const [first, ...rest] = items;
|
|
33
|
+
|
|
34
|
+
// Spread operator
|
|
35
|
+
const merged = { ...defaults, ...options };
|
|
36
|
+
const combined = [...array1, ...array2];
|
|
37
|
+
|
|
38
|
+
// Template literals
|
|
39
|
+
const message = `Hello, ${name}!`;
|
|
40
|
+
|
|
41
|
+
// Optional chaining
|
|
42
|
+
const avatar = user?.profile?.avatar ?? defaultAvatar;
|
|
43
|
+
|
|
44
|
+
// Nullish coalescing
|
|
45
|
+
const value = input ?? defaultValue;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Async/Await
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
// Async functions
|
|
52
|
+
async function fetchUser(id) {
|
|
53
|
+
const response = await fetch(`/api/users/${id}`);
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
throw new Error(`Failed to fetch user: ${response.status}`);
|
|
56
|
+
}
|
|
57
|
+
return response.json();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Error handling
|
|
61
|
+
async function processUser(id) {
|
|
62
|
+
try {
|
|
63
|
+
const user = await fetchUser(id);
|
|
64
|
+
return await processData(user);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error("Processing failed:", error);
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Parallel execution
|
|
72
|
+
const [users, posts] = await Promise.all([
|
|
73
|
+
fetchUsers(),
|
|
74
|
+
fetchPosts()
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
// Promise.allSettled for partial failures
|
|
78
|
+
const results = await Promise.allSettled([
|
|
79
|
+
fetchUser(1),
|
|
80
|
+
fetchUser(2),
|
|
81
|
+
fetchUser(3)
|
|
82
|
+
]);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Classes
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
class UserService {
|
|
89
|
+
#repository; // Private field
|
|
90
|
+
|
|
91
|
+
constructor(repository) {
|
|
92
|
+
this.#repository = repository;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async create(data) {
|
|
96
|
+
const user = new User(data);
|
|
97
|
+
await this.#validate(user);
|
|
98
|
+
return this.#repository.save(user);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async #validate(user) {
|
|
102
|
+
if (!user.email) {
|
|
103
|
+
throw new ValidationError("Email required");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static fromConfig(config) {
|
|
108
|
+
return new UserService(new Repository(config));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Error Handling
|
|
114
|
+
|
|
115
|
+
```javascript
|
|
116
|
+
// Custom errors
|
|
117
|
+
class NotFoundError extends Error {
|
|
118
|
+
constructor(id) {
|
|
119
|
+
super(`Not found: ${id}`);
|
|
120
|
+
this.name = "NotFoundError";
|
|
121
|
+
this.id = id;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
class ValidationError extends Error {
|
|
126
|
+
constructor(message, field) {
|
|
127
|
+
super(message);
|
|
128
|
+
this.name = "ValidationError";
|
|
129
|
+
this.field = field;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Error handling
|
|
134
|
+
async function handleRequest(req) {
|
|
135
|
+
try {
|
|
136
|
+
return await processRequest(req);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (error instanceof NotFoundError) {
|
|
139
|
+
return { status: 404, message: error.message };
|
|
140
|
+
}
|
|
141
|
+
if (error instanceof ValidationError) {
|
|
142
|
+
return { status: 400, message: error.message, field: error.field };
|
|
143
|
+
}
|
|
144
|
+
throw error; // Re-throw unexpected errors
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Modules
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
// Named exports
|
|
153
|
+
export function createUser(data) { }
|
|
154
|
+
export const MAX_USERS = 100;
|
|
155
|
+
|
|
156
|
+
// Default export
|
|
157
|
+
export default class UserService { }
|
|
158
|
+
|
|
159
|
+
// Imports
|
|
160
|
+
import UserService from "./user-service.js";
|
|
161
|
+
import { createUser, MAX_USERS } from "./utils.js";
|
|
162
|
+
import * as utils from "./utils.js";
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Array Methods
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
// Map, filter, reduce
|
|
169
|
+
const emails = users.map(u => u.email);
|
|
170
|
+
const active = users.filter(u => u.isActive);
|
|
171
|
+
const total = orders.reduce((sum, o) => sum + o.total, 0);
|
|
172
|
+
|
|
173
|
+
// Find
|
|
174
|
+
const user = users.find(u => u.id === targetId);
|
|
175
|
+
const index = users.findIndex(u => u.id === targetId);
|
|
176
|
+
|
|
177
|
+
// Some/every
|
|
178
|
+
const hasAdmin = users.some(u => u.role === "admin");
|
|
179
|
+
const allActive = users.every(u => u.isActive);
|
|
180
|
+
|
|
181
|
+
// Chaining
|
|
182
|
+
const result = users
|
|
183
|
+
.filter(u => u.isActive)
|
|
184
|
+
.map(u => u.email)
|
|
185
|
+
.sort();
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Testing (Jest)
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
describe("UserService", () => {
|
|
192
|
+
let repository;
|
|
193
|
+
let service;
|
|
194
|
+
|
|
195
|
+
beforeEach(() => {
|
|
196
|
+
repository = { save: jest.fn(), find: jest.fn() };
|
|
197
|
+
service = new UserService(repository);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe("create", () => {
|
|
201
|
+
it("creates user with valid data", async () => {
|
|
202
|
+
repository.save.mockResolvedValue({ id: "1", email: "test@example.com" });
|
|
203
|
+
|
|
204
|
+
const user = await service.create({ email: "test@example.com" });
|
|
205
|
+
|
|
206
|
+
expect(user.email).toBe("test@example.com");
|
|
207
|
+
expect(repository.save).toHaveBeenCalled();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("throws on invalid email", async () => {
|
|
211
|
+
await expect(service.create({ email: "" }))
|
|
212
|
+
.rejects
|
|
213
|
+
.toThrow(ValidationError);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
```
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# JavaScript Testing (Jest)
|
|
2
|
+
|
|
3
|
+
## Project Structure
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
myproject/
|
|
7
|
+
├── src/
|
|
8
|
+
│ └── services/
|
|
9
|
+
│ └── userService.js
|
|
10
|
+
└── tests/ # or __tests__/
|
|
11
|
+
└── services/
|
|
12
|
+
└── userService.test.js
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Basic Tests
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
const { UserService } = require("../src/services/userService");
|
|
19
|
+
|
|
20
|
+
describe("UserService", () => {
|
|
21
|
+
let service;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
service = new UserService();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe("create", () => {
|
|
28
|
+
it("creates user with valid email", async () => {
|
|
29
|
+
const user = await service.create({ email: "test@example.com" });
|
|
30
|
+
|
|
31
|
+
expect(user.email).toBe("test@example.com");
|
|
32
|
+
expect(user.id).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("throws on invalid email", async () => {
|
|
36
|
+
await expect(service.create({ email: "invalid" }))
|
|
37
|
+
.rejects
|
|
38
|
+
.toThrow(ValidationError);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Matchers
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
// Equality
|
|
48
|
+
expect(value).toBe(exact); // ===
|
|
49
|
+
expect(value).toEqual(deepEqual); // deep equality
|
|
50
|
+
expect(value).toStrictEqual(obj); // deep + type
|
|
51
|
+
|
|
52
|
+
// Truthiness
|
|
53
|
+
expect(value).toBeTruthy();
|
|
54
|
+
expect(value).toBeFalsy();
|
|
55
|
+
expect(value).toBeNull();
|
|
56
|
+
expect(value).toBeUndefined();
|
|
57
|
+
expect(value).toBeDefined();
|
|
58
|
+
|
|
59
|
+
// Numbers
|
|
60
|
+
expect(value).toBeGreaterThan(3);
|
|
61
|
+
expect(value).toBeLessThanOrEqual(5);
|
|
62
|
+
expect(value).toBeCloseTo(0.3, 5); // floating point
|
|
63
|
+
|
|
64
|
+
// Strings
|
|
65
|
+
expect(string).toMatch(/pattern/);
|
|
66
|
+
expect(string).toContain("substring");
|
|
67
|
+
|
|
68
|
+
// Arrays
|
|
69
|
+
expect(array).toContain(item);
|
|
70
|
+
expect(array).toHaveLength(3);
|
|
71
|
+
expect(array).toContainEqual({ id: 1 });
|
|
72
|
+
|
|
73
|
+
// Objects
|
|
74
|
+
expect(object).toHaveProperty("name");
|
|
75
|
+
expect(object).toHaveProperty("user.email", "test@example.com");
|
|
76
|
+
expect(object).toMatchObject({ name: "John" });
|
|
77
|
+
|
|
78
|
+
// Exceptions
|
|
79
|
+
expect(() => fn()).toThrow();
|
|
80
|
+
expect(() => fn()).toThrow(ErrorType);
|
|
81
|
+
expect(() => fn()).toThrow("message");
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Async Testing
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
// Async/await
|
|
88
|
+
it("fetches user", async () => {
|
|
89
|
+
const user = await service.fetch("123");
|
|
90
|
+
expect(user.id).toBe("123");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Promises
|
|
94
|
+
it("fetches user", () => {
|
|
95
|
+
return service.fetch("123").then(user => {
|
|
96
|
+
expect(user.id).toBe("123");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Rejections
|
|
101
|
+
it("rejects on not found", async () => {
|
|
102
|
+
await expect(service.fetch("invalid"))
|
|
103
|
+
.rejects
|
|
104
|
+
.toThrow(NotFoundError);
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Mocking
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
// Mock functions
|
|
112
|
+
const mockFn = jest.fn();
|
|
113
|
+
mockFn.mockReturnValue(42);
|
|
114
|
+
mockFn.mockResolvedValue(user);
|
|
115
|
+
mockFn.mockRejectedValue(new Error("failed"));
|
|
116
|
+
|
|
117
|
+
// Mock implementations
|
|
118
|
+
mockFn.mockImplementation((x) => x * 2);
|
|
119
|
+
|
|
120
|
+
// Verify calls
|
|
121
|
+
expect(mockFn).toHaveBeenCalled();
|
|
122
|
+
expect(mockFn).toHaveBeenCalledWith("arg1", "arg2");
|
|
123
|
+
expect(mockFn).toHaveBeenCalledTimes(2);
|
|
124
|
+
|
|
125
|
+
// Mock modules
|
|
126
|
+
jest.mock("../src/services/emailService");
|
|
127
|
+
const { EmailService } = require("../src/services/emailService");
|
|
128
|
+
EmailService.prototype.send = jest.fn().mockResolvedValue(true);
|
|
129
|
+
|
|
130
|
+
// Spy on existing methods
|
|
131
|
+
const spy = jest.spyOn(console, "log");
|
|
132
|
+
// ... test
|
|
133
|
+
spy.mockRestore();
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Testing with Mocked Dependencies
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
describe("UserService", () => {
|
|
140
|
+
let repository;
|
|
141
|
+
let notifier;
|
|
142
|
+
let service;
|
|
143
|
+
|
|
144
|
+
beforeEach(() => {
|
|
145
|
+
repository = {
|
|
146
|
+
save: jest.fn(),
|
|
147
|
+
find: jest.fn()
|
|
148
|
+
};
|
|
149
|
+
notifier = {
|
|
150
|
+
sendWelcome: jest.fn().mockResolvedValue(true)
|
|
151
|
+
};
|
|
152
|
+
service = new UserService(repository, notifier);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("saves user and sends welcome", async () => {
|
|
156
|
+
const userData = { email: "test@example.com" };
|
|
157
|
+
repository.save.mockResolvedValue({ id: "1", ...userData });
|
|
158
|
+
|
|
159
|
+
const user = await service.create(userData);
|
|
160
|
+
|
|
161
|
+
expect(repository.save).toHaveBeenCalledWith(expect.objectContaining({
|
|
162
|
+
email: "test@example.com"
|
|
163
|
+
}));
|
|
164
|
+
expect(notifier.sendWelcome).toHaveBeenCalledWith(user);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Setup and Teardown
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
beforeAll(() => {
|
|
173
|
+
// Run once before all tests
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
beforeEach(() => {
|
|
177
|
+
// Run before each test
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
afterEach(() => {
|
|
181
|
+
// Run after each test
|
|
182
|
+
jest.clearAllMocks();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
afterAll(() => {
|
|
186
|
+
// Run once after all tests
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Snapshot Testing
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
it("renders correctly", () => {
|
|
194
|
+
const tree = renderer.create(<Button label="Click me" />).toJSON();
|
|
195
|
+
expect(tree).toMatchSnapshot();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Inline snapshot
|
|
199
|
+
it("serializes correctly", () => {
|
|
200
|
+
expect(user.toJSON()).toMatchInlineSnapshot(`
|
|
201
|
+
{
|
|
202
|
+
"email": "test@example.com",
|
|
203
|
+
"id": "1"
|
|
204
|
+
}
|
|
205
|
+
`);
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Test Each (Parameterized)
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
describe("validateEmail", () => {
|
|
213
|
+
it.each([
|
|
214
|
+
["test@example.com", true],
|
|
215
|
+
["invalid", false],
|
|
216
|
+
["", false]
|
|
217
|
+
])("validates %s as %s", (email, expected) => {
|
|
218
|
+
expect(validateEmail(email)).toBe(expected);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// With named parameters
|
|
222
|
+
it.each`
|
|
223
|
+
email | expected
|
|
224
|
+
${"test@example.com"} | ${true}
|
|
225
|
+
${"invalid"} | ${false}
|
|
226
|
+
`("validates $email", ({ email, expected }) => {
|
|
227
|
+
expect(validateEmail(email)).toBe(expected);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Timer Mocking
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
jest.useFakeTimers();
|
|
236
|
+
|
|
237
|
+
it("calls callback after delay", () => {
|
|
238
|
+
const callback = jest.fn();
|
|
239
|
+
|
|
240
|
+
delayedCall(callback, 1000);
|
|
241
|
+
|
|
242
|
+
expect(callback).not.toHaveBeenCalled();
|
|
243
|
+
jest.advanceTimersByTime(1000);
|
|
244
|
+
expect(callback).toHaveBeenCalled();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
afterEach(() => {
|
|
248
|
+
jest.useRealTimers();
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Running Tests
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
# Run all tests
|
|
256
|
+
npm test
|
|
257
|
+
|
|
258
|
+
# Watch mode
|
|
259
|
+
npm test -- --watch
|
|
260
|
+
|
|
261
|
+
# Coverage
|
|
262
|
+
npm test -- --coverage
|
|
263
|
+
|
|
264
|
+
# Run specific file
|
|
265
|
+
npm test -- userService.test.js
|
|
266
|
+
|
|
267
|
+
# Run matching tests
|
|
268
|
+
npm test -- -t "creates user"
|
|
269
|
+
```
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Python Async/Await
|
|
2
|
+
|
|
3
|
+
## Basic Async Patterns
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
import asyncio
|
|
7
|
+
from typing import List
|
|
8
|
+
|
|
9
|
+
# ✅ Async function definition
|
|
10
|
+
async def fetch_user(user_id: int) -> User:
|
|
11
|
+
async with aiohttp.ClientSession() as session:
|
|
12
|
+
async with session.get(f"/users/{user_id}") as response:
|
|
13
|
+
data = await response.json()
|
|
14
|
+
return User(**data)
|
|
15
|
+
|
|
16
|
+
# ✅ Running async code
|
|
17
|
+
async def main():
|
|
18
|
+
user = await fetch_user(1)
|
|
19
|
+
print(user.name)
|
|
20
|
+
|
|
21
|
+
asyncio.run(main())
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Parallel Execution
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
# ❌ Sequential (slow)
|
|
28
|
+
async def get_all_users(ids: List[int]) -> List[User]:
|
|
29
|
+
users = []
|
|
30
|
+
for user_id in ids:
|
|
31
|
+
user = await fetch_user(user_id) # One at a time
|
|
32
|
+
users.append(user)
|
|
33
|
+
return users
|
|
34
|
+
|
|
35
|
+
# ✅ Parallel with gather (fast)
|
|
36
|
+
async def get_all_users(ids: List[int]) -> List[User]:
|
|
37
|
+
tasks = [fetch_user(user_id) for user_id in ids]
|
|
38
|
+
return await asyncio.gather(*tasks) # All at once
|
|
39
|
+
|
|
40
|
+
# ✅ Parallel with error handling
|
|
41
|
+
async def get_all_users_safe(ids: List[int]) -> List[User | Exception]:
|
|
42
|
+
tasks = [fetch_user(user_id) for user_id in ids]
|
|
43
|
+
return await asyncio.gather(*tasks, return_exceptions=True)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Async Context Managers
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from contextlib import asynccontextmanager
|
|
50
|
+
|
|
51
|
+
# ✅ Async context manager
|
|
52
|
+
@asynccontextmanager
|
|
53
|
+
async def get_connection():
|
|
54
|
+
conn = await database.connect()
|
|
55
|
+
try:
|
|
56
|
+
yield conn
|
|
57
|
+
finally:
|
|
58
|
+
await conn.close()
|
|
59
|
+
|
|
60
|
+
# Usage
|
|
61
|
+
async def query_users():
|
|
62
|
+
async with get_connection() as conn:
|
|
63
|
+
return await conn.execute("SELECT * FROM users")
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Timeouts and Cancellation
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# ✅ Add timeouts to prevent hanging
|
|
70
|
+
async def fetch_with_timeout(url: str, timeout: float = 10.0):
|
|
71
|
+
try:
|
|
72
|
+
async with asyncio.timeout(timeout):
|
|
73
|
+
return await fetch(url)
|
|
74
|
+
except asyncio.TimeoutError:
|
|
75
|
+
raise ServiceError(f"Request to {url} timed out")
|
|
76
|
+
|
|
77
|
+
# ✅ Handle cancellation gracefully
|
|
78
|
+
async def long_running_task():
|
|
79
|
+
try:
|
|
80
|
+
while True:
|
|
81
|
+
await process_batch()
|
|
82
|
+
await asyncio.sleep(1)
|
|
83
|
+
except asyncio.CancelledError:
|
|
84
|
+
await cleanup()
|
|
85
|
+
raise
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Semaphore for Rate Limiting
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# ✅ Limit concurrent operations
|
|
92
|
+
async def fetch_all(urls: List[str], max_concurrent: int = 10):
|
|
93
|
+
semaphore = asyncio.Semaphore(max_concurrent)
|
|
94
|
+
|
|
95
|
+
async def fetch_one(url: str):
|
|
96
|
+
async with semaphore:
|
|
97
|
+
return await fetch(url)
|
|
98
|
+
|
|
99
|
+
return await asyncio.gather(*[fetch_one(url) for url in urls])
|
|
100
|
+
```
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Python Fundamentals
|
|
2
|
+
|
|
3
|
+
## PEP 8 Style Guide
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
# Naming conventions
|
|
7
|
+
variable_name = "snake_case" # Variables and functions
|
|
8
|
+
CONSTANT_VALUE = 42 # Constants
|
|
9
|
+
ClassName = "PascalCase" # Classes
|
|
10
|
+
_private_var = "internal" # Private by convention
|
|
11
|
+
|
|
12
|
+
# Imports - group and order
|
|
13
|
+
import os # 1. Standard library
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
import requests # 2. Third-party
|
|
17
|
+
from flask import Flask
|
|
18
|
+
|
|
19
|
+
from myapp.models import User # 3. Local application
|
|
20
|
+
from myapp.utils import helper
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## String Formatting
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
# ✅ Use f-strings (Python 3.6+)
|
|
27
|
+
name = "Alice"
|
|
28
|
+
age = 30
|
|
29
|
+
message = f"Hello, {name}! You are {age} years old."
|
|
30
|
+
|
|
31
|
+
# ✅ Multi-line f-strings
|
|
32
|
+
query = f"""
|
|
33
|
+
SELECT *
|
|
34
|
+
FROM users
|
|
35
|
+
WHERE name = '{name}'
|
|
36
|
+
AND age > {age - 5}
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# ❌ Avoid old-style formatting
|
|
40
|
+
message = "Hello, %s!" % name # Old style
|
|
41
|
+
message = "Hello, {}!".format(name) # Verbose
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## List Comprehensions
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
# ✅ Simple and readable
|
|
48
|
+
squares = [x ** 2 for x in range(10)]
|
|
49
|
+
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
|
|
50
|
+
|
|
51
|
+
# ❌ Too complex - use regular loop instead
|
|
52
|
+
result = [
|
|
53
|
+
transform(item)
|
|
54
|
+
for sublist in nested_list
|
|
55
|
+
for item in sublist
|
|
56
|
+
if condition(item) and another_condition(item)
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
# ✅ Break into steps when complex
|
|
60
|
+
filtered_items = [item for sublist in nested_list for item in sublist]
|
|
61
|
+
filtered_items = [item for item in filtered_items if condition(item)]
|
|
62
|
+
result = [transform(item) for item in filtered_items]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Context Managers
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
# ✅ Always use context managers for resources
|
|
69
|
+
with open('file.txt', 'r') as f:
|
|
70
|
+
content = f.read()
|
|
71
|
+
# File automatically closed
|
|
72
|
+
|
|
73
|
+
# ✅ Database connections
|
|
74
|
+
with db.connection() as conn:
|
|
75
|
+
cursor = conn.execute(query)
|
|
76
|
+
|
|
77
|
+
# ✅ Multiple resources
|
|
78
|
+
with open('input.txt') as infile, open('output.txt', 'w') as outfile:
|
|
79
|
+
outfile.write(infile.read())
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Pythonic Patterns
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
# ✅ EAFP (Easier to Ask Forgiveness than Permission)
|
|
86
|
+
try:
|
|
87
|
+
value = my_dict[key]
|
|
88
|
+
except KeyError:
|
|
89
|
+
value = default
|
|
90
|
+
|
|
91
|
+
# Or simply:
|
|
92
|
+
value = my_dict.get(key, default)
|
|
93
|
+
|
|
94
|
+
# ✅ Truthiness
|
|
95
|
+
if my_list: # Instead of: if len(my_list) > 0
|
|
96
|
+
process(my_list)
|
|
97
|
+
|
|
98
|
+
if name: # Instead of: if name != ""
|
|
99
|
+
greet(name)
|
|
100
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Python Language Guidelines
|
|
2
|
+
|
|
3
|
+
This directory contains Python-specific coding guidelines.
|
|
4
|
+
|
|
5
|
+
## Available Chunks
|
|
6
|
+
|
|
7
|
+
- **basics.md** - PEP 8, string formatting, comprehensions, context managers
|
|
8
|
+
- **types.md** - Type hints, TypedDict, Protocols, generics
|
|
9
|
+
- **async.md** - async/await, asyncio, parallel execution
|
|
10
|
+
- **testing.md** - pytest fixtures, parametrized tests, mocking
|