@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
package/data/index.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# C# Fundamentals
|
|
2
|
+
|
|
3
|
+
## Project Structure
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
MyProject/
|
|
7
|
+
├── src/
|
|
8
|
+
│ ├── MyProject.Api/
|
|
9
|
+
│ │ ├── Controllers/
|
|
10
|
+
│ │ ├── Program.cs
|
|
11
|
+
│ │ └── MyProject.Api.csproj
|
|
12
|
+
│ ├── MyProject.Core/
|
|
13
|
+
│ │ ├── Entities/
|
|
14
|
+
│ │ ├── Interfaces/
|
|
15
|
+
│ │ └── Services/
|
|
16
|
+
│ └── MyProject.Infrastructure/
|
|
17
|
+
│ ├── Data/
|
|
18
|
+
│ └── Services/
|
|
19
|
+
├── tests/
|
|
20
|
+
│ └── MyProject.Tests/
|
|
21
|
+
└── MyProject.sln
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Naming Conventions
|
|
25
|
+
|
|
26
|
+
```csharp
|
|
27
|
+
// Classes, interfaces, methods: PascalCase
|
|
28
|
+
public class UserService { }
|
|
29
|
+
public interface IUserRepository { }
|
|
30
|
+
public void CreateUser() { }
|
|
31
|
+
|
|
32
|
+
// Interface prefix: I
|
|
33
|
+
public interface IRepository { }
|
|
34
|
+
|
|
35
|
+
// Private fields: _camelCase
|
|
36
|
+
private readonly IUserRepository _repository;
|
|
37
|
+
|
|
38
|
+
// Parameters, local variables: camelCase
|
|
39
|
+
public User GetUser(string userId) { }
|
|
40
|
+
|
|
41
|
+
// Constants: PascalCase
|
|
42
|
+
public const int MaxRetries = 3;
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Modern C# Features (10+)
|
|
46
|
+
|
|
47
|
+
```csharp
|
|
48
|
+
// Records for immutable data
|
|
49
|
+
public record User(string Id, string Email, string Name);
|
|
50
|
+
|
|
51
|
+
// Pattern matching
|
|
52
|
+
var result = obj switch
|
|
53
|
+
{
|
|
54
|
+
User u when u.IsActive => "Active user",
|
|
55
|
+
User u => "Inactive user",
|
|
56
|
+
null => "No user",
|
|
57
|
+
_ => "Unknown"
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Null handling
|
|
61
|
+
string? nullableString = GetValue();
|
|
62
|
+
string nonNull = nullableString ?? "default";
|
|
63
|
+
int length = nullableString?.Length ?? 0;
|
|
64
|
+
|
|
65
|
+
// Target-typed new
|
|
66
|
+
List<User> users = new();
|
|
67
|
+
Dictionary<string, int> counts = new();
|
|
68
|
+
|
|
69
|
+
// File-scoped namespaces
|
|
70
|
+
namespace MyProject.Services;
|
|
71
|
+
|
|
72
|
+
public class UserService { }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Async/Await
|
|
76
|
+
|
|
77
|
+
```csharp
|
|
78
|
+
// Async methods return Task
|
|
79
|
+
public async Task<User> GetUserAsync(string id)
|
|
80
|
+
{
|
|
81
|
+
var user = await _repository.FindByIdAsync(id);
|
|
82
|
+
return user ?? throw new NotFoundException(id);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Async enumerable
|
|
86
|
+
public async IAsyncEnumerable<User> GetUsersAsync()
|
|
87
|
+
{
|
|
88
|
+
await foreach (var user in _repository.StreamAsync())
|
|
89
|
+
{
|
|
90
|
+
yield return user;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Cancellation
|
|
95
|
+
public async Task ProcessAsync(CancellationToken ct)
|
|
96
|
+
{
|
|
97
|
+
ct.ThrowIfCancellationRequested();
|
|
98
|
+
await _service.DoWorkAsync(ct);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## LINQ
|
|
103
|
+
|
|
104
|
+
```csharp
|
|
105
|
+
// Query syntax
|
|
106
|
+
var activeUsers = from u in users
|
|
107
|
+
where u.IsActive
|
|
108
|
+
orderby u.Name
|
|
109
|
+
select u;
|
|
110
|
+
|
|
111
|
+
// Method syntax (preferred)
|
|
112
|
+
var emails = users
|
|
113
|
+
.Where(u => u.IsActive)
|
|
114
|
+
.OrderBy(u => u.Name)
|
|
115
|
+
.Select(u => u.Email)
|
|
116
|
+
.ToList();
|
|
117
|
+
|
|
118
|
+
// Grouping
|
|
119
|
+
var byDepartment = users
|
|
120
|
+
.GroupBy(u => u.Department)
|
|
121
|
+
.ToDictionary(g => g.Key, g => g.ToList());
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Exception Handling
|
|
125
|
+
|
|
126
|
+
```csharp
|
|
127
|
+
// Custom exceptions
|
|
128
|
+
public class NotFoundException : Exception
|
|
129
|
+
{
|
|
130
|
+
public NotFoundException(string id)
|
|
131
|
+
: base($"Entity not found: {id}") { }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Try-catch-finally
|
|
135
|
+
try
|
|
136
|
+
{
|
|
137
|
+
await ProcessAsync();
|
|
138
|
+
}
|
|
139
|
+
catch (NotFoundException ex) when (ex.Message.Contains("user"))
|
|
140
|
+
{
|
|
141
|
+
_logger.LogWarning(ex, "User not found");
|
|
142
|
+
throw;
|
|
143
|
+
}
|
|
144
|
+
catch (Exception ex)
|
|
145
|
+
{
|
|
146
|
+
_logger.LogError(ex, "Unexpected error");
|
|
147
|
+
throw;
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Dependency Injection
|
|
152
|
+
|
|
153
|
+
```csharp
|
|
154
|
+
// Service registration
|
|
155
|
+
builder.Services.AddScoped<IUserRepository, UserRepository>();
|
|
156
|
+
builder.Services.AddScoped<IUserService, UserService>();
|
|
157
|
+
|
|
158
|
+
// Constructor injection
|
|
159
|
+
public class UserService : IUserService
|
|
160
|
+
{
|
|
161
|
+
private readonly IUserRepository _repository;
|
|
162
|
+
private readonly ILogger<UserService> _logger;
|
|
163
|
+
|
|
164
|
+
public UserService(IUserRepository repository, ILogger<UserService> logger)
|
|
165
|
+
{
|
|
166
|
+
_repository = repository;
|
|
167
|
+
_logger = logger;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Testing (xUnit)
|
|
173
|
+
|
|
174
|
+
```csharp
|
|
175
|
+
public class UserServiceTests
|
|
176
|
+
{
|
|
177
|
+
private readonly Mock<IUserRepository> _repositoryMock;
|
|
178
|
+
private readonly UserService _sut;
|
|
179
|
+
|
|
180
|
+
public UserServiceTests()
|
|
181
|
+
{
|
|
182
|
+
_repositoryMock = new Mock<IUserRepository>();
|
|
183
|
+
_sut = new UserService(_repositoryMock.Object);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
[Fact]
|
|
187
|
+
public async Task GetUser_WhenExists_ReturnsUser()
|
|
188
|
+
{
|
|
189
|
+
// Arrange
|
|
190
|
+
var user = new User("1", "test@example.com", "Test");
|
|
191
|
+
_repositoryMock.Setup(r => r.FindByIdAsync("1"))
|
|
192
|
+
.ReturnsAsync(user);
|
|
193
|
+
|
|
194
|
+
// Act
|
|
195
|
+
var result = await _sut.GetUserAsync("1");
|
|
196
|
+
|
|
197
|
+
// Assert
|
|
198
|
+
Assert.Equal("test@example.com", result.Email);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
[Theory]
|
|
202
|
+
[InlineData("")]
|
|
203
|
+
[InlineData(null)]
|
|
204
|
+
public async Task GetUser_WhenInvalidId_ThrowsArgumentException(string id)
|
|
205
|
+
{
|
|
206
|
+
await Assert.ThrowsAsync<ArgumentException>(
|
|
207
|
+
() => _sut.GetUserAsync(id));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# C# Testing (xUnit)
|
|
2
|
+
|
|
3
|
+
## Test Structure
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
MyProject/
|
|
7
|
+
├── src/
|
|
8
|
+
│ └── MyProject/
|
|
9
|
+
│ └── Services/UserService.cs
|
|
10
|
+
└── tests/
|
|
11
|
+
└── MyProject.Tests/
|
|
12
|
+
└── Services/UserServiceTests.cs
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Basic Tests
|
|
16
|
+
|
|
17
|
+
```csharp
|
|
18
|
+
using Xunit;
|
|
19
|
+
|
|
20
|
+
public class UserServiceTests
|
|
21
|
+
{
|
|
22
|
+
private readonly UserService _sut; // System Under Test
|
|
23
|
+
|
|
24
|
+
public UserServiceTests()
|
|
25
|
+
{
|
|
26
|
+
_sut = new UserService();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
[Fact]
|
|
30
|
+
public void Create_WithValidEmail_ReturnsUser()
|
|
31
|
+
{
|
|
32
|
+
var user = _sut.Create("test@example.com");
|
|
33
|
+
|
|
34
|
+
Assert.NotNull(user);
|
|
35
|
+
Assert.Equal("test@example.com", user.Email);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
[Fact]
|
|
39
|
+
public void Create_WithInvalidEmail_ThrowsValidationException()
|
|
40
|
+
{
|
|
41
|
+
Assert.Throws<ValidationException>(() => _sut.Create("invalid"));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Assertions
|
|
47
|
+
|
|
48
|
+
```csharp
|
|
49
|
+
// Equality
|
|
50
|
+
Assert.Equal(expected, actual);
|
|
51
|
+
Assert.NotEqual(unexpected, actual);
|
|
52
|
+
|
|
53
|
+
// Boolean
|
|
54
|
+
Assert.True(condition);
|
|
55
|
+
Assert.False(condition);
|
|
56
|
+
|
|
57
|
+
// Null
|
|
58
|
+
Assert.Null(value);
|
|
59
|
+
Assert.NotNull(value);
|
|
60
|
+
|
|
61
|
+
// Collections
|
|
62
|
+
Assert.Empty(collection);
|
|
63
|
+
Assert.NotEmpty(collection);
|
|
64
|
+
Assert.Contains(item, collection);
|
|
65
|
+
Assert.DoesNotContain(item, collection);
|
|
66
|
+
Assert.All(collection, item => Assert.NotNull(item.Name));
|
|
67
|
+
|
|
68
|
+
// Type
|
|
69
|
+
Assert.IsType<User>(result);
|
|
70
|
+
Assert.IsAssignableFrom<IEntity>(result);
|
|
71
|
+
|
|
72
|
+
// Exceptions
|
|
73
|
+
var ex = Assert.Throws<NotFoundException>(() => service.Find("invalid"));
|
|
74
|
+
Assert.Equal("User not found", ex.Message);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Theory Tests (Parameterized)
|
|
78
|
+
|
|
79
|
+
```csharp
|
|
80
|
+
[Theory]
|
|
81
|
+
[InlineData("test@example.com", true)]
|
|
82
|
+
[InlineData("invalid", false)]
|
|
83
|
+
[InlineData("", false)]
|
|
84
|
+
public void IsValidEmail_WithVariousInputs_ReturnsExpected(string email, bool expected)
|
|
85
|
+
{
|
|
86
|
+
var result = _validator.IsValid(email);
|
|
87
|
+
Assert.Equal(expected, result);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
[Theory]
|
|
91
|
+
[MemberData(nameof(GetTestUsers))]
|
|
92
|
+
public void ProcessUser_WithVariousUsers_ReturnsExpected(User user, string expectedStatus)
|
|
93
|
+
{
|
|
94
|
+
var result = _processor.Process(user);
|
|
95
|
+
Assert.Equal(expectedStatus, result);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public static IEnumerable<object[]> GetTestUsers()
|
|
99
|
+
{
|
|
100
|
+
yield return new object[] { new User("active@example.com") { IsActive = true }, "processed" };
|
|
101
|
+
yield return new object[] { new User("inactive@example.com") { IsActive = false }, "skipped" };
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Test Classes and Collections
|
|
106
|
+
|
|
107
|
+
```csharp
|
|
108
|
+
// Shared context across tests in a class
|
|
109
|
+
public class UserServiceTests : IClassFixture<DatabaseFixture>
|
|
110
|
+
{
|
|
111
|
+
private readonly DatabaseFixture _fixture;
|
|
112
|
+
|
|
113
|
+
public UserServiceTests(DatabaseFixture fixture)
|
|
114
|
+
{
|
|
115
|
+
_fixture = fixture;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
[Fact]
|
|
119
|
+
public void Test1() { /* uses _fixture */ }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Shared context across multiple test classes
|
|
123
|
+
[CollectionDefinition("Database")]
|
|
124
|
+
public class DatabaseCollection : ICollectionFixture<DatabaseFixture> { }
|
|
125
|
+
|
|
126
|
+
[Collection("Database")]
|
|
127
|
+
public class UserServiceTests { }
|
|
128
|
+
|
|
129
|
+
[Collection("Database")]
|
|
130
|
+
public class OrderServiceTests { }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Mocking with Moq
|
|
134
|
+
|
|
135
|
+
```csharp
|
|
136
|
+
using Moq;
|
|
137
|
+
|
|
138
|
+
public class UserServiceTests
|
|
139
|
+
{
|
|
140
|
+
private readonly Mock<IUserRepository> _repositoryMock;
|
|
141
|
+
private readonly Mock<ILogger<UserService>> _loggerMock;
|
|
142
|
+
private readonly UserService _sut;
|
|
143
|
+
|
|
144
|
+
public UserServiceTests()
|
|
145
|
+
{
|
|
146
|
+
_repositoryMock = new Mock<IUserRepository>();
|
|
147
|
+
_loggerMock = new Mock<ILogger<UserService>>();
|
|
148
|
+
_sut = new UserService(_repositoryMock.Object, _loggerMock.Object);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
[Fact]
|
|
152
|
+
public async Task GetUser_WhenExists_ReturnsUser()
|
|
153
|
+
{
|
|
154
|
+
// Arrange
|
|
155
|
+
var user = new User("1", "test@example.com");
|
|
156
|
+
_repositoryMock.Setup(r => r.FindByIdAsync("1"))
|
|
157
|
+
.ReturnsAsync(user);
|
|
158
|
+
|
|
159
|
+
// Act
|
|
160
|
+
var result = await _sut.GetUserAsync("1");
|
|
161
|
+
|
|
162
|
+
// Assert
|
|
163
|
+
Assert.Equal("test@example.com", result.Email);
|
|
164
|
+
_repositoryMock.Verify(r => r.FindByIdAsync("1"), Times.Once);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
[Fact]
|
|
168
|
+
public async Task GetUser_WhenNotFound_ThrowsNotFoundException()
|
|
169
|
+
{
|
|
170
|
+
_repositoryMock.Setup(r => r.FindByIdAsync(It.IsAny<string>()))
|
|
171
|
+
.ReturnsAsync((User?)null);
|
|
172
|
+
|
|
173
|
+
await Assert.ThrowsAsync<NotFoundException>(
|
|
174
|
+
() => _sut.GetUserAsync("invalid"));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Async Testing
|
|
180
|
+
|
|
181
|
+
```csharp
|
|
182
|
+
[Fact]
|
|
183
|
+
public async Task CreateAsync_WithValidData_ReturnsUser()
|
|
184
|
+
{
|
|
185
|
+
var user = await _sut.CreateAsync("test@example.com");
|
|
186
|
+
|
|
187
|
+
Assert.NotNull(user);
|
|
188
|
+
Assert.Equal("test@example.com", user.Email);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
[Fact]
|
|
192
|
+
public async Task CreateAsync_WithTimeout_CompletesInTime()
|
|
193
|
+
{
|
|
194
|
+
var task = _sut.CreateAsync("test@example.com");
|
|
195
|
+
var completed = await Task.WhenAny(task, Task.Delay(5000)) == task;
|
|
196
|
+
|
|
197
|
+
Assert.True(completed, "Operation timed out");
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Test Output
|
|
202
|
+
|
|
203
|
+
```csharp
|
|
204
|
+
public class UserServiceTests
|
|
205
|
+
{
|
|
206
|
+
private readonly ITestOutputHelper _output;
|
|
207
|
+
|
|
208
|
+
public UserServiceTests(ITestOutputHelper output)
|
|
209
|
+
{
|
|
210
|
+
_output = output;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
[Fact]
|
|
214
|
+
public void Test_WithLogging()
|
|
215
|
+
{
|
|
216
|
+
_output.WriteLine("Starting test...");
|
|
217
|
+
// test logic
|
|
218
|
+
_output.WriteLine($"Result: {result}");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Integration Testing with WebApplicationFactory
|
|
224
|
+
|
|
225
|
+
```csharp
|
|
226
|
+
public class UserApiTests : IClassFixture<WebApplicationFactory<Program>>
|
|
227
|
+
{
|
|
228
|
+
private readonly HttpClient _client;
|
|
229
|
+
|
|
230
|
+
public UserApiTests(WebApplicationFactory<Program> factory)
|
|
231
|
+
{
|
|
232
|
+
_client = factory.WithWebHostBuilder(builder =>
|
|
233
|
+
{
|
|
234
|
+
builder.ConfigureServices(services =>
|
|
235
|
+
{
|
|
236
|
+
// Replace real services with test doubles
|
|
237
|
+
services.AddScoped<IUserRepository, InMemoryUserRepository>();
|
|
238
|
+
});
|
|
239
|
+
}).CreateClient();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
[Fact]
|
|
243
|
+
public async Task GetUser_ReturnsUser()
|
|
244
|
+
{
|
|
245
|
+
var response = await _client.GetAsync("/api/users/1");
|
|
246
|
+
|
|
247
|
+
response.EnsureSuccessStatusCode();
|
|
248
|
+
var user = await response.Content.ReadFromJsonAsync<User>();
|
|
249
|
+
Assert.Equal("test@example.com", user?.Email);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Go Fundamentals
|
|
2
|
+
|
|
3
|
+
## Project Structure
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
myproject/
|
|
7
|
+
├── cmd/
|
|
8
|
+
│ └── myapp/
|
|
9
|
+
│ └── main.go # Application entry point
|
|
10
|
+
├── internal/ # Private application code
|
|
11
|
+
│ ├── handler/
|
|
12
|
+
│ ├── service/
|
|
13
|
+
│ └── repository/
|
|
14
|
+
├── pkg/ # Public library code
|
|
15
|
+
├── go.mod
|
|
16
|
+
└── go.sum
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Naming Conventions
|
|
20
|
+
|
|
21
|
+
```go
|
|
22
|
+
// Package names: lowercase, single word
|
|
23
|
+
package user
|
|
24
|
+
|
|
25
|
+
// Exported (public): PascalCase
|
|
26
|
+
func CreateUser() {}
|
|
27
|
+
type UserService struct {}
|
|
28
|
+
|
|
29
|
+
// Unexported (private): camelCase
|
|
30
|
+
func validateEmail() {}
|
|
31
|
+
type userCache struct {}
|
|
32
|
+
|
|
33
|
+
// Interfaces: -er suffix when possible
|
|
34
|
+
type Reader interface {}
|
|
35
|
+
type UserRepository interface {}
|
|
36
|
+
|
|
37
|
+
// Constants: PascalCase for exported
|
|
38
|
+
const MaxRetries = 3
|
|
39
|
+
const defaultTimeout = 30
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Error Handling
|
|
43
|
+
|
|
44
|
+
```go
|
|
45
|
+
// Always check errors
|
|
46
|
+
result, err := doSomething()
|
|
47
|
+
if err != nil {
|
|
48
|
+
return fmt.Errorf("failed to do something: %w", err)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Custom errors
|
|
52
|
+
var ErrNotFound = errors.New("not found")
|
|
53
|
+
var ErrInvalidInput = errors.New("invalid input")
|
|
54
|
+
|
|
55
|
+
// Error wrapping for context
|
|
56
|
+
if err != nil {
|
|
57
|
+
return fmt.Errorf("creating user %s: %w", name, err)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Errors.Is and errors.As for checking
|
|
61
|
+
if errors.Is(err, ErrNotFound) {
|
|
62
|
+
// Handle not found
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Concurrency
|
|
67
|
+
|
|
68
|
+
```go
|
|
69
|
+
// Goroutines for concurrent work
|
|
70
|
+
go func() {
|
|
71
|
+
// Concurrent task
|
|
72
|
+
}()
|
|
73
|
+
|
|
74
|
+
// Channels for communication
|
|
75
|
+
ch := make(chan int)
|
|
76
|
+
ch <- value // Send
|
|
77
|
+
value := <-ch // Receive
|
|
78
|
+
|
|
79
|
+
// Context for cancellation
|
|
80
|
+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
81
|
+
defer cancel()
|
|
82
|
+
|
|
83
|
+
// sync.WaitGroup for coordination
|
|
84
|
+
var wg sync.WaitGroup
|
|
85
|
+
wg.Add(1)
|
|
86
|
+
go func() {
|
|
87
|
+
defer wg.Done()
|
|
88
|
+
// Work
|
|
89
|
+
}()
|
|
90
|
+
wg.Wait()
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Testing
|
|
94
|
+
|
|
95
|
+
```go
|
|
96
|
+
// Test file: user_test.go
|
|
97
|
+
func TestCreateUser(t *testing.T) {
|
|
98
|
+
// Arrange
|
|
99
|
+
svc := NewUserService()
|
|
100
|
+
|
|
101
|
+
// Act
|
|
102
|
+
user, err := svc.Create("test@example.com")
|
|
103
|
+
|
|
104
|
+
// Assert
|
|
105
|
+
if err != nil {
|
|
106
|
+
t.Fatalf("unexpected error: %v", err)
|
|
107
|
+
}
|
|
108
|
+
if user.Email != "test@example.com" {
|
|
109
|
+
t.Errorf("got %s, want test@example.com", user.Email)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Table-driven tests
|
|
114
|
+
func TestValidateEmail(t *testing.T) {
|
|
115
|
+
tests := []struct {
|
|
116
|
+
name string
|
|
117
|
+
email string
|
|
118
|
+
valid bool
|
|
119
|
+
}{
|
|
120
|
+
{"valid", "test@example.com", true},
|
|
121
|
+
{"no at", "invalid", false},
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for _, tt := range tests {
|
|
125
|
+
t.Run(tt.name, func(t *testing.T) {
|
|
126
|
+
got := ValidateEmail(tt.email)
|
|
127
|
+
if got != tt.valid {
|
|
128
|
+
t.Errorf("ValidateEmail(%q) = %v, want %v", tt.email, got, tt.valid)
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Common Patterns
|
|
136
|
+
|
|
137
|
+
```go
|
|
138
|
+
// Functional options
|
|
139
|
+
type Option func(*Config)
|
|
140
|
+
|
|
141
|
+
func WithTimeout(d time.Duration) Option {
|
|
142
|
+
return func(c *Config) { c.Timeout = d }
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
func NewClient(opts ...Option) *Client {
|
|
146
|
+
cfg := defaultConfig()
|
|
147
|
+
for _, opt := range opts {
|
|
148
|
+
opt(&cfg)
|
|
149
|
+
}
|
|
150
|
+
return &Client{config: cfg}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Interface-based design
|
|
154
|
+
type UserRepository interface {
|
|
155
|
+
FindByID(ctx context.Context, id string) (*User, error)
|
|
156
|
+
Save(ctx context.Context, user *User) error
|
|
157
|
+
}
|
|
158
|
+
```
|