@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,192 @@
|
|
|
1
|
+
# Go Testing
|
|
2
|
+
|
|
3
|
+
## Test File Structure
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
myproject/
|
|
7
|
+
├── user.go
|
|
8
|
+
├── user_test.go # Tests alongside source
|
|
9
|
+
├── user_internal_test.go # Internal tests (same package)
|
|
10
|
+
└── testdata/ # Test fixtures
|
|
11
|
+
└── fixtures.json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Test Functions
|
|
15
|
+
|
|
16
|
+
```go
|
|
17
|
+
// Basic test
|
|
18
|
+
func TestCreateUser(t *testing.T) {
|
|
19
|
+
user := CreateUser("test@example.com")
|
|
20
|
+
|
|
21
|
+
if user.Email != "test@example.com" {
|
|
22
|
+
t.Errorf("expected email %q, got %q", "test@example.com", user.Email)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Table-driven tests (preferred)
|
|
27
|
+
func TestValidateEmail(t *testing.T) {
|
|
28
|
+
tests := []struct {
|
|
29
|
+
name string
|
|
30
|
+
email string
|
|
31
|
+
wantErr bool
|
|
32
|
+
}{
|
|
33
|
+
{"valid email", "test@example.com", false},
|
|
34
|
+
{"missing @", "invalid", true},
|
|
35
|
+
{"empty", "", true},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for _, tt := range tests {
|
|
39
|
+
t.Run(tt.name, func(t *testing.T) {
|
|
40
|
+
err := ValidateEmail(tt.email)
|
|
41
|
+
if (err != nil) != tt.wantErr {
|
|
42
|
+
t.Errorf("ValidateEmail(%q) error = %v, wantErr %v",
|
|
43
|
+
tt.email, err, tt.wantErr)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Subtests and Parallel
|
|
51
|
+
|
|
52
|
+
```go
|
|
53
|
+
func TestUserService(t *testing.T) {
|
|
54
|
+
t.Run("Create", func(t *testing.T) {
|
|
55
|
+
t.Parallel() // Run in parallel
|
|
56
|
+
// test create
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
t.Run("Update", func(t *testing.T) {
|
|
60
|
+
t.Parallel()
|
|
61
|
+
// test update
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Test Helpers
|
|
67
|
+
|
|
68
|
+
```go
|
|
69
|
+
// Helper function (call t.Helper() first)
|
|
70
|
+
func createTestUser(t *testing.T, email string) *User {
|
|
71
|
+
t.Helper()
|
|
72
|
+
user, err := NewUser(email)
|
|
73
|
+
if err != nil {
|
|
74
|
+
t.Fatalf("failed to create test user: %v", err)
|
|
75
|
+
}
|
|
76
|
+
return user
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Cleanup
|
|
80
|
+
func TestWithTempFile(t *testing.T) {
|
|
81
|
+
f, err := os.CreateTemp("", "test")
|
|
82
|
+
if err != nil {
|
|
83
|
+
t.Fatal(err)
|
|
84
|
+
}
|
|
85
|
+
t.Cleanup(func() { os.Remove(f.Name()) })
|
|
86
|
+
|
|
87
|
+
// use f...
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Mocking with Interfaces
|
|
92
|
+
|
|
93
|
+
```go
|
|
94
|
+
// Define interface for dependency
|
|
95
|
+
type UserRepository interface {
|
|
96
|
+
Save(user *User) error
|
|
97
|
+
FindByID(id string) (*User, error)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Mock implementation
|
|
101
|
+
type mockRepository struct {
|
|
102
|
+
users map[string]*User
|
|
103
|
+
saveErr error
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func (m *mockRepository) Save(user *User) error {
|
|
107
|
+
if m.saveErr != nil {
|
|
108
|
+
return m.saveErr
|
|
109
|
+
}
|
|
110
|
+
m.users[user.ID] = user
|
|
111
|
+
return nil
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
func (m *mockRepository) FindByID(id string) (*User, error) {
|
|
115
|
+
if user, ok := m.users[id]; ok {
|
|
116
|
+
return user, nil
|
|
117
|
+
}
|
|
118
|
+
return nil, ErrNotFound
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Use in tests
|
|
122
|
+
func TestUserService_Create(t *testing.T) {
|
|
123
|
+
repo := &mockRepository{users: make(map[string]*User)}
|
|
124
|
+
service := NewUserService(repo)
|
|
125
|
+
|
|
126
|
+
user, err := service.Create("test@example.com")
|
|
127
|
+
|
|
128
|
+
if err != nil {
|
|
129
|
+
t.Fatalf("unexpected error: %v", err)
|
|
130
|
+
}
|
|
131
|
+
if _, exists := repo.users[user.ID]; !exists {
|
|
132
|
+
t.Error("user not saved to repository")
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Benchmarks
|
|
138
|
+
|
|
139
|
+
```go
|
|
140
|
+
func BenchmarkProcessUsers(b *testing.B) {
|
|
141
|
+
users := generateTestUsers(1000)
|
|
142
|
+
b.ResetTimer()
|
|
143
|
+
|
|
144
|
+
for i := 0; i < b.N; i++ {
|
|
145
|
+
ProcessUsers(users)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Run: go test -bench=. -benchmem
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Test Coverage
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Generate coverage
|
|
156
|
+
go test -coverprofile=coverage.out ./...
|
|
157
|
+
|
|
158
|
+
# View coverage report
|
|
159
|
+
go tool cover -html=coverage.out
|
|
160
|
+
|
|
161
|
+
# Check coverage percentage
|
|
162
|
+
go test -cover ./...
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Integration Tests
|
|
166
|
+
|
|
167
|
+
```go
|
|
168
|
+
//go:build integration
|
|
169
|
+
|
|
170
|
+
package mypackage_test
|
|
171
|
+
|
|
172
|
+
import (
|
|
173
|
+
"testing"
|
|
174
|
+
"database/sql"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
func TestDatabaseIntegration(t *testing.T) {
|
|
178
|
+
if testing.Short() {
|
|
179
|
+
t.Skip("skipping integration test")
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
db, err := sql.Open("postgres", os.Getenv("TEST_DATABASE_URL"))
|
|
183
|
+
if err != nil {
|
|
184
|
+
t.Fatal(err)
|
|
185
|
+
}
|
|
186
|
+
t.Cleanup(func() { db.Close() })
|
|
187
|
+
|
|
188
|
+
// integration tests...
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Run: go test -tags=integration ./...
|
|
192
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Language-Specific Guidelines
|
|
2
|
+
|
|
3
|
+
Programming language best practices and idioms.
|
|
4
|
+
|
|
5
|
+
## Available Languages
|
|
6
|
+
|
|
7
|
+
- `typescript/` - TypeScript guidelines
|
|
8
|
+
- `javascript/` - JavaScript (ES6+) guidelines
|
|
9
|
+
- `python/` - Python guidelines
|
|
10
|
+
- `go/` - Go guidelines
|
|
11
|
+
- `rust/` - Rust guidelines
|
|
12
|
+
- `java/` - Java guidelines
|
|
13
|
+
- `csharp/` - C# guidelines
|
|
14
|
+
- `ruby/` - Ruby guidelines
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Java Fundamentals
|
|
2
|
+
|
|
3
|
+
## Project Structure (Maven/Gradle)
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
myproject/
|
|
7
|
+
├── src/
|
|
8
|
+
│ ├── main/
|
|
9
|
+
│ │ ├── java/
|
|
10
|
+
│ │ │ └── com/example/
|
|
11
|
+
│ │ │ ├── Application.java
|
|
12
|
+
│ │ │ ├── controller/
|
|
13
|
+
│ │ │ ├── service/
|
|
14
|
+
│ │ │ ├── repository/
|
|
15
|
+
│ │ │ └── model/
|
|
16
|
+
│ │ └── resources/
|
|
17
|
+
│ │ └── application.yml
|
|
18
|
+
│ └── test/
|
|
19
|
+
│ └── java/
|
|
20
|
+
├── pom.xml (Maven) or build.gradle (Gradle)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Naming Conventions
|
|
24
|
+
|
|
25
|
+
```java
|
|
26
|
+
// Classes: PascalCase
|
|
27
|
+
public class UserService {}
|
|
28
|
+
|
|
29
|
+
// Interfaces: PascalCase, often -able suffix
|
|
30
|
+
public interface Comparable {}
|
|
31
|
+
public interface UserRepository {}
|
|
32
|
+
|
|
33
|
+
// Methods/variables: camelCase
|
|
34
|
+
public void createUser() {}
|
|
35
|
+
private String userName;
|
|
36
|
+
|
|
37
|
+
// Constants: UPPER_SNAKE_CASE
|
|
38
|
+
public static final int MAX_RETRIES = 3;
|
|
39
|
+
|
|
40
|
+
// Packages: lowercase
|
|
41
|
+
package com.example.service;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Modern Java Features (17+)
|
|
45
|
+
|
|
46
|
+
```java
|
|
47
|
+
// Records for immutable data
|
|
48
|
+
public record User(String id, String email, String name) {}
|
|
49
|
+
|
|
50
|
+
// Pattern matching
|
|
51
|
+
if (obj instanceof String s) {
|
|
52
|
+
System.out.println(s.length());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Switch expressions
|
|
56
|
+
String result = switch (status) {
|
|
57
|
+
case ACTIVE -> "Active";
|
|
58
|
+
case PENDING -> "Pending";
|
|
59
|
+
case DELETED -> "Deleted";
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Text blocks
|
|
63
|
+
String json = """
|
|
64
|
+
{
|
|
65
|
+
"name": "test",
|
|
66
|
+
"value": 123
|
|
67
|
+
}
|
|
68
|
+
""";
|
|
69
|
+
|
|
70
|
+
// Sealed classes
|
|
71
|
+
public sealed interface Shape permits Circle, Rectangle {}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Exception Handling
|
|
75
|
+
|
|
76
|
+
```java
|
|
77
|
+
// Checked exceptions
|
|
78
|
+
public User findUser(String id) throws UserNotFoundException {
|
|
79
|
+
return repository.findById(id)
|
|
80
|
+
.orElseThrow(() -> new UserNotFoundException(id));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Try-with-resources
|
|
84
|
+
try (var reader = new BufferedReader(new FileReader(path))) {
|
|
85
|
+
return reader.readLine();
|
|
86
|
+
} catch (IOException e) {
|
|
87
|
+
throw new DataAccessException("Failed to read file", e);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Custom exceptions
|
|
91
|
+
public class UserNotFoundException extends RuntimeException {
|
|
92
|
+
public UserNotFoundException(String id) {
|
|
93
|
+
super("User not found: " + id);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Optional
|
|
99
|
+
|
|
100
|
+
```java
|
|
101
|
+
// Avoid null, use Optional
|
|
102
|
+
public Optional<User> findUser(String id) {
|
|
103
|
+
return Optional.ofNullable(repository.find(id));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Chaining
|
|
107
|
+
findUser(id)
|
|
108
|
+
.map(User::getEmail)
|
|
109
|
+
.filter(email -> email.contains("@"))
|
|
110
|
+
.orElse("unknown@example.com");
|
|
111
|
+
|
|
112
|
+
// Never use Optional in fields or parameters
|
|
113
|
+
// Only for return types
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Streams
|
|
117
|
+
|
|
118
|
+
```java
|
|
119
|
+
// Filter, map, collect
|
|
120
|
+
List<String> emails = users.stream()
|
|
121
|
+
.filter(u -> u.isActive())
|
|
122
|
+
.map(User::getEmail)
|
|
123
|
+
.collect(Collectors.toList());
|
|
124
|
+
|
|
125
|
+
// Grouping
|
|
126
|
+
Map<String, List<User>> byDepartment = users.stream()
|
|
127
|
+
.collect(Collectors.groupingBy(User::getDepartment));
|
|
128
|
+
|
|
129
|
+
// Reduce
|
|
130
|
+
int totalAge = users.stream()
|
|
131
|
+
.mapToInt(User::getAge)
|
|
132
|
+
.sum();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Testing (JUnit 5)
|
|
136
|
+
|
|
137
|
+
```java
|
|
138
|
+
@ExtendWith(MockitoExtension.class)
|
|
139
|
+
class UserServiceTest {
|
|
140
|
+
|
|
141
|
+
@Mock
|
|
142
|
+
private UserRepository repository;
|
|
143
|
+
|
|
144
|
+
@InjectMocks
|
|
145
|
+
private UserService service;
|
|
146
|
+
|
|
147
|
+
@Test
|
|
148
|
+
void shouldCreateUser() {
|
|
149
|
+
// Arrange
|
|
150
|
+
var request = new CreateUserRequest("test@example.com");
|
|
151
|
+
when(repository.save(any())).thenReturn(new User("1", "test@example.com"));
|
|
152
|
+
|
|
153
|
+
// Act
|
|
154
|
+
var user = service.create(request);
|
|
155
|
+
|
|
156
|
+
// Assert
|
|
157
|
+
assertThat(user.getEmail()).isEqualTo("test@example.com");
|
|
158
|
+
verify(repository).save(any());
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@ParameterizedTest
|
|
162
|
+
@ValueSource(strings = {"invalid", "no-at", ""})
|
|
163
|
+
void shouldRejectInvalidEmails(String email) {
|
|
164
|
+
assertThrows(ValidationException.class,
|
|
165
|
+
() -> service.create(new CreateUserRequest(email)));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Dependency Injection (Spring)
|
|
171
|
+
|
|
172
|
+
```java
|
|
173
|
+
@Service
|
|
174
|
+
public class UserService {
|
|
175
|
+
private final UserRepository repository;
|
|
176
|
+
private final EmailService emailService;
|
|
177
|
+
|
|
178
|
+
// Constructor injection (preferred)
|
|
179
|
+
public UserService(UserRepository repository, EmailService emailService) {
|
|
180
|
+
this.repository = repository;
|
|
181
|
+
this.emailService = emailService;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# Java Testing (JUnit 5)
|
|
2
|
+
|
|
3
|
+
## Test Structure
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
src/
|
|
7
|
+
├── main/java/com/example/
|
|
8
|
+
│ └── UserService.java
|
|
9
|
+
└── test/java/com/example/
|
|
10
|
+
└── UserServiceTest.java
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Basic Tests
|
|
14
|
+
|
|
15
|
+
```java
|
|
16
|
+
import org.junit.jupiter.api.*;
|
|
17
|
+
import static org.junit.jupiter.api.Assertions.*;
|
|
18
|
+
|
|
19
|
+
class UserServiceTest {
|
|
20
|
+
|
|
21
|
+
private UserService service;
|
|
22
|
+
|
|
23
|
+
@BeforeEach
|
|
24
|
+
void setUp() {
|
|
25
|
+
service = new UserService();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@Test
|
|
29
|
+
void createUser_WithValidEmail_ReturnsUser() {
|
|
30
|
+
User user = service.create("test@example.com");
|
|
31
|
+
|
|
32
|
+
assertEquals("test@example.com", user.getEmail());
|
|
33
|
+
assertNotNull(user.getId());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Test
|
|
37
|
+
void createUser_WithInvalidEmail_ThrowsException() {
|
|
38
|
+
assertThrows(ValidationException.class, () -> {
|
|
39
|
+
service.create("invalid");
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Assertions
|
|
46
|
+
|
|
47
|
+
```java
|
|
48
|
+
// Equality
|
|
49
|
+
assertEquals(expected, actual);
|
|
50
|
+
assertNotEquals(unexpected, actual);
|
|
51
|
+
|
|
52
|
+
// Boolean
|
|
53
|
+
assertTrue(condition);
|
|
54
|
+
assertFalse(condition);
|
|
55
|
+
|
|
56
|
+
// Null checks
|
|
57
|
+
assertNull(value);
|
|
58
|
+
assertNotNull(value);
|
|
59
|
+
|
|
60
|
+
// Collections
|
|
61
|
+
assertIterableEquals(expected, actual);
|
|
62
|
+
assertArrayEquals(expectedArray, actualArray);
|
|
63
|
+
|
|
64
|
+
// Multiple assertions (all run even if some fail)
|
|
65
|
+
assertAll(
|
|
66
|
+
() -> assertEquals("John", user.getName()),
|
|
67
|
+
() -> assertEquals("john@example.com", user.getEmail()),
|
|
68
|
+
() -> assertTrue(user.isActive())
|
|
69
|
+
);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Parameterized Tests
|
|
73
|
+
|
|
74
|
+
```java
|
|
75
|
+
@ParameterizedTest
|
|
76
|
+
@ValueSource(strings = {"", " ", "invalid", "no-at-sign"})
|
|
77
|
+
void validateEmail_WithInvalidInput_ReturnsFalse(String email) {
|
|
78
|
+
assertFalse(validator.isValid(email));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@ParameterizedTest
|
|
82
|
+
@CsvSource({
|
|
83
|
+
"test@example.com, true",
|
|
84
|
+
"invalid, false",
|
|
85
|
+
"'', false"
|
|
86
|
+
})
|
|
87
|
+
void validateEmail_WithVariousInputs(String email, boolean expected) {
|
|
88
|
+
assertEquals(expected, validator.isValid(email));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@ParameterizedTest
|
|
92
|
+
@MethodSource("provideUsersForTest")
|
|
93
|
+
void processUser_WithVariousUsers(User user, String expectedStatus) {
|
|
94
|
+
assertEquals(expectedStatus, processor.process(user));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static Stream<Arguments> provideUsersForTest() {
|
|
98
|
+
return Stream.of(
|
|
99
|
+
Arguments.of(new User("active@example.com", true), "processed"),
|
|
100
|
+
Arguments.of(new User("inactive@example.com", false), "skipped")
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Nested Tests
|
|
106
|
+
|
|
107
|
+
```java
|
|
108
|
+
@DisplayName("UserService")
|
|
109
|
+
class UserServiceTest {
|
|
110
|
+
|
|
111
|
+
@Nested
|
|
112
|
+
@DisplayName("create")
|
|
113
|
+
class Create {
|
|
114
|
+
|
|
115
|
+
@Test
|
|
116
|
+
@DisplayName("with valid email creates user")
|
|
117
|
+
void withValidEmail() {
|
|
118
|
+
// test
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@Test
|
|
122
|
+
@DisplayName("with duplicate email throws exception")
|
|
123
|
+
void withDuplicateEmail() {
|
|
124
|
+
// test
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@Nested
|
|
129
|
+
@DisplayName("delete")
|
|
130
|
+
class Delete {
|
|
131
|
+
// delete tests
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Mocking with Mockito
|
|
137
|
+
|
|
138
|
+
```java
|
|
139
|
+
import org.mockito.*;
|
|
140
|
+
import static org.mockito.Mockito.*;
|
|
141
|
+
|
|
142
|
+
@ExtendWith(MockitoExtension.class)
|
|
143
|
+
class UserServiceTest {
|
|
144
|
+
|
|
145
|
+
@Mock
|
|
146
|
+
private UserRepository repository;
|
|
147
|
+
|
|
148
|
+
@InjectMocks
|
|
149
|
+
private UserService service;
|
|
150
|
+
|
|
151
|
+
@Test
|
|
152
|
+
void create_SavesUserToRepository() {
|
|
153
|
+
User user = new User("test@example.com");
|
|
154
|
+
when(repository.save(any(User.class))).thenReturn(user);
|
|
155
|
+
|
|
156
|
+
User result = service.create("test@example.com");
|
|
157
|
+
|
|
158
|
+
verify(repository).save(any(User.class));
|
|
159
|
+
assertEquals("test@example.com", result.getEmail());
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@Test
|
|
163
|
+
void findById_WhenNotFound_ThrowsException() {
|
|
164
|
+
when(repository.findById("123")).thenReturn(Optional.empty());
|
|
165
|
+
|
|
166
|
+
assertThrows(NotFoundException.class, () -> {
|
|
167
|
+
service.findById("123");
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Lifecycle Hooks
|
|
174
|
+
|
|
175
|
+
```java
|
|
176
|
+
@BeforeAll
|
|
177
|
+
static void initAll() {
|
|
178
|
+
// Run once before all tests
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@BeforeEach
|
|
182
|
+
void init() {
|
|
183
|
+
// Run before each test
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@AfterEach
|
|
187
|
+
void tearDown() {
|
|
188
|
+
// Run after each test
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@AfterAll
|
|
192
|
+
static void tearDownAll() {
|
|
193
|
+
// Run once after all tests
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Async Testing
|
|
198
|
+
|
|
199
|
+
```java
|
|
200
|
+
@Test
|
|
201
|
+
void asyncOperation_CompletesWithinTimeout() {
|
|
202
|
+
assertTimeout(Duration.ofSeconds(2), () -> {
|
|
203
|
+
service.longRunningOperation();
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@Test
|
|
208
|
+
void asyncOperation_WithCompletableFuture() throws Exception {
|
|
209
|
+
CompletableFuture<User> future = service.createAsync("test@example.com");
|
|
210
|
+
|
|
211
|
+
User user = future.get(5, TimeUnit.SECONDS);
|
|
212
|
+
assertEquals("test@example.com", user.getEmail());
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Test Tags and Filtering
|
|
217
|
+
|
|
218
|
+
```java
|
|
219
|
+
@Tag("integration")
|
|
220
|
+
@Test
|
|
221
|
+
void databaseIntegrationTest() {
|
|
222
|
+
// integration test
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@Tag("slow")
|
|
226
|
+
@Test
|
|
227
|
+
void slowPerformanceTest() {
|
|
228
|
+
// slow test
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Run: mvn test -Dgroups="integration"
|
|
232
|
+
// Exclude: mvn test -DexcludedGroups="slow"
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Spring Boot Testing
|
|
236
|
+
|
|
237
|
+
```java
|
|
238
|
+
@SpringBootTest
|
|
239
|
+
class UserServiceIntegrationTest {
|
|
240
|
+
|
|
241
|
+
@Autowired
|
|
242
|
+
private UserService service;
|
|
243
|
+
|
|
244
|
+
@MockBean
|
|
245
|
+
private EmailService emailService;
|
|
246
|
+
|
|
247
|
+
@Test
|
|
248
|
+
void create_SendsWelcomeEmail() {
|
|
249
|
+
service.create("test@example.com");
|
|
250
|
+
|
|
251
|
+
verify(emailService).sendWelcome(any(User.class));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@WebMvcTest(UserController.class)
|
|
256
|
+
class UserControllerTest {
|
|
257
|
+
|
|
258
|
+
@Autowired
|
|
259
|
+
private MockMvc mockMvc;
|
|
260
|
+
|
|
261
|
+
@MockBean
|
|
262
|
+
private UserService service;
|
|
263
|
+
|
|
264
|
+
@Test
|
|
265
|
+
void getUser_ReturnsUser() throws Exception {
|
|
266
|
+
when(service.findById("1")).thenReturn(new User("1", "test@example.com"));
|
|
267
|
+
|
|
268
|
+
mockMvc.perform(get("/users/1"))
|
|
269
|
+
.andExpect(status().isOk())
|
|
270
|
+
.andExpect(jsonPath("$.email").value("test@example.com"));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|