@pratikpsl/agent-skills 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -201
- package/package.json +2 -2
- package/src/templates/dotnet/AgentSkills/README.md +40 -40
- package/src/templates/dotnet/AgentSkills/agents/README.md +11 -11
- package/src/templates/dotnet/AgentSkills/agents/architect.agent.md +59 -59
- package/src/templates/dotnet/AgentSkills/agents/developer.agent.md +88 -88
- package/src/templates/dotnet/AgentSkills/memory/index.md +21 -21
- package/src/templates/dotnet/AgentSkills/memory/lessons/api.md +3 -3
- package/src/templates/dotnet/AgentSkills/memory/lessons/auth.md +3 -3
- package/src/templates/dotnet/AgentSkills/memory/lessons/csharp.md +3 -3
- package/src/templates/dotnet/AgentSkills/memory/lessons/db.md +3 -3
- package/src/templates/dotnet/AgentSkills/memory/lessons/design.md +3 -3
- package/src/templates/dotnet/AgentSkills/memory/lessons/infra.md +2 -2
- package/src/templates/dotnet/AgentSkills/memory/lessons/mcp.md +3 -3
- package/src/templates/dotnet/AgentSkills/memory/lessons/testing.md +3 -3
- package/src/templates/dotnet/AgentSkills/memory/schema.md +26 -26
- package/src/templates/dotnet/AgentSkills/skills/INDEX.md +8 -8
- package/src/templates/dotnet/AgentSkills/skills/code-standards/SKILL.md +122 -122
- package/src/templates/dotnet/AgentSkills/skills/core/SKILL.md +23 -23
- package/src/templates/dotnet/AgentSkills/skills/csharp-xunit/SKILL.md +67 -67
- package/src/templates/dotnet/AgentSkills/skills/design/SKILL.md +108 -108
- package/src/templates/dotnet/AgentSkills/skills/dotnet-api/SKILL.md +303 -303
- package/src/templates/dotnet/AgentSkills/skills/dotnet-best-practices/SKILL.md +170 -170
- package/src/templates/dotnet/AgentSkills/skills/harness/SKILL.md +62 -62
- package/src/templates/dotnet/AgentSkills/skills/mcp_dotnet/SKILL.md +54 -54
- package/src/templates/dotnet/manifest.json +1 -1
|
@@ -1,122 +1,122 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: code-standards
|
|
3
|
-
description: >
|
|
4
|
-
Load when writing, reviewing, or refactoring C# code.
|
|
5
|
-
Covers naming, structure, comments, and commit hygiene.
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Code Standards
|
|
9
|
-
|
|
10
|
-
Language: C#. Every example is production code, not pseudocode.
|
|
11
|
-
|
|
12
|
-
## 1. No Magic Values
|
|
13
|
-
|
|
14
|
-
```csharp
|
|
15
|
-
// Bad
|
|
16
|
-
if (role == 3) { }
|
|
17
|
-
|
|
18
|
-
// Good
|
|
19
|
-
private const int AdminRoleId = 3;
|
|
20
|
-
if (role == AdminRoleId) { }
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## 2. Meaningful Names
|
|
24
|
-
|
|
25
|
-
Names explain what a value represents and why it matters. If a comment is needed to explain a variable, rename it.
|
|
26
|
-
|
|
27
|
-
```csharp
|
|
28
|
-
// Bad
|
|
29
|
-
int d = 86400;
|
|
30
|
-
bool flag = true;
|
|
31
|
-
|
|
32
|
-
// Good
|
|
33
|
-
const int SecondsPerDay = 86400;
|
|
34
|
-
bool isUserVerified = true;
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## 3. One Function, One Responsibility
|
|
38
|
-
|
|
39
|
-
One function should do one thing. If you cannot summarize it in one sentence, split it.
|
|
40
|
-
|
|
41
|
-
## 4. Descriptive Booleans
|
|
42
|
-
|
|
43
|
-
```csharp
|
|
44
|
-
// Bad
|
|
45
|
-
bool check = true;
|
|
46
|
-
|
|
47
|
-
// Good
|
|
48
|
-
bool hasPermission = true;
|
|
49
|
-
bool isSessionExpired = false;
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## 5. DRY
|
|
53
|
-
|
|
54
|
-
Keep one source of truth per piece of logic. Apply judgment: premature abstraction is also harmful.
|
|
55
|
-
|
|
56
|
-
## 6. Early Returns Over Deep Nesting
|
|
57
|
-
|
|
58
|
-
```csharp
|
|
59
|
-
// Bad
|
|
60
|
-
void Process(User user)
|
|
61
|
-
{
|
|
62
|
-
if (user != null)
|
|
63
|
-
{
|
|
64
|
-
if (user.IsActive)
|
|
65
|
-
{
|
|
66
|
-
// Main logic
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Good
|
|
72
|
-
void Process(User user)
|
|
73
|
-
{
|
|
74
|
-
if (user is null) return;
|
|
75
|
-
if (!user.IsActive) return;
|
|
76
|
-
|
|
77
|
-
// Main logic
|
|
78
|
-
}
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## 7. Comment Why, Not What
|
|
82
|
-
|
|
83
|
-
```csharp
|
|
84
|
-
// Bad
|
|
85
|
-
// Increment counter.
|
|
86
|
-
count++;
|
|
87
|
-
|
|
88
|
-
// Good
|
|
89
|
-
// Back off before HTTP 429; this API caps requests at 100 per minute.
|
|
90
|
-
if (requestCount >= RateLimitBackoffThreshold) { }
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## 8. Limit Function Arguments
|
|
94
|
-
|
|
95
|
-
More than 3 arguments should usually become a config object, command object, or record.
|
|
96
|
-
|
|
97
|
-
```csharp
|
|
98
|
-
// Bad
|
|
99
|
-
CreateUser("Alice", "alice@example.com", true, "admin", "UTC");
|
|
100
|
-
|
|
101
|
-
// Good
|
|
102
|
-
CreateUser(new UserConfig
|
|
103
|
-
{
|
|
104
|
-
Name = "Alice",
|
|
105
|
-
Email = "alice@example.com",
|
|
106
|
-
IsVerified = true,
|
|
107
|
-
Role = Role.Admin,
|
|
108
|
-
Timezone = "UTC"
|
|
109
|
-
});
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## 9. Meaningful Commits
|
|
113
|
-
|
|
114
|
-
```text
|
|
115
|
-
Bad:
|
|
116
|
-
fix bug
|
|
117
|
-
update stuff
|
|
118
|
-
|
|
119
|
-
Good:
|
|
120
|
-
fix: prevent double-submit on payment
|
|
121
|
-
refactor: extract shared email validation
|
|
122
|
-
```
|
|
1
|
+
---
|
|
2
|
+
name: code-standards
|
|
3
|
+
description: >
|
|
4
|
+
Load when writing, reviewing, or refactoring C# code.
|
|
5
|
+
Covers naming, structure, comments, and commit hygiene.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Code Standards
|
|
9
|
+
|
|
10
|
+
Language: C#. Every example is production code, not pseudocode.
|
|
11
|
+
|
|
12
|
+
## 1. No Magic Values
|
|
13
|
+
|
|
14
|
+
```csharp
|
|
15
|
+
// Bad
|
|
16
|
+
if (role == 3) { }
|
|
17
|
+
|
|
18
|
+
// Good
|
|
19
|
+
private const int AdminRoleId = 3;
|
|
20
|
+
if (role == AdminRoleId) { }
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 2. Meaningful Names
|
|
24
|
+
|
|
25
|
+
Names explain what a value represents and why it matters. If a comment is needed to explain a variable, rename it.
|
|
26
|
+
|
|
27
|
+
```csharp
|
|
28
|
+
// Bad
|
|
29
|
+
int d = 86400;
|
|
30
|
+
bool flag = true;
|
|
31
|
+
|
|
32
|
+
// Good
|
|
33
|
+
const int SecondsPerDay = 86400;
|
|
34
|
+
bool isUserVerified = true;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 3. One Function, One Responsibility
|
|
38
|
+
|
|
39
|
+
One function should do one thing. If you cannot summarize it in one sentence, split it.
|
|
40
|
+
|
|
41
|
+
## 4. Descriptive Booleans
|
|
42
|
+
|
|
43
|
+
```csharp
|
|
44
|
+
// Bad
|
|
45
|
+
bool check = true;
|
|
46
|
+
|
|
47
|
+
// Good
|
|
48
|
+
bool hasPermission = true;
|
|
49
|
+
bool isSessionExpired = false;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 5. DRY
|
|
53
|
+
|
|
54
|
+
Keep one source of truth per piece of logic. Apply judgment: premature abstraction is also harmful.
|
|
55
|
+
|
|
56
|
+
## 6. Early Returns Over Deep Nesting
|
|
57
|
+
|
|
58
|
+
```csharp
|
|
59
|
+
// Bad
|
|
60
|
+
void Process(User user)
|
|
61
|
+
{
|
|
62
|
+
if (user != null)
|
|
63
|
+
{
|
|
64
|
+
if (user.IsActive)
|
|
65
|
+
{
|
|
66
|
+
// Main logic
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Good
|
|
72
|
+
void Process(User user)
|
|
73
|
+
{
|
|
74
|
+
if (user is null) return;
|
|
75
|
+
if (!user.IsActive) return;
|
|
76
|
+
|
|
77
|
+
// Main logic
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 7. Comment Why, Not What
|
|
82
|
+
|
|
83
|
+
```csharp
|
|
84
|
+
// Bad
|
|
85
|
+
// Increment counter.
|
|
86
|
+
count++;
|
|
87
|
+
|
|
88
|
+
// Good
|
|
89
|
+
// Back off before HTTP 429; this API caps requests at 100 per minute.
|
|
90
|
+
if (requestCount >= RateLimitBackoffThreshold) { }
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 8. Limit Function Arguments
|
|
94
|
+
|
|
95
|
+
More than 3 arguments should usually become a config object, command object, or record.
|
|
96
|
+
|
|
97
|
+
```csharp
|
|
98
|
+
// Bad
|
|
99
|
+
CreateUser("Alice", "alice@example.com", true, "admin", "UTC");
|
|
100
|
+
|
|
101
|
+
// Good
|
|
102
|
+
CreateUser(new UserConfig
|
|
103
|
+
{
|
|
104
|
+
Name = "Alice",
|
|
105
|
+
Email = "alice@example.com",
|
|
106
|
+
IsVerified = true,
|
|
107
|
+
Role = Role.Admin,
|
|
108
|
+
Timezone = "UTC"
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 9. Meaningful Commits
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
Bad:
|
|
116
|
+
fix bug
|
|
117
|
+
update stuff
|
|
118
|
+
|
|
119
|
+
Good:
|
|
120
|
+
fix: prevent double-submit on payment
|
|
121
|
+
refactor: extract shared email validation
|
|
122
|
+
```
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: core
|
|
3
|
-
description: >
|
|
4
|
-
Load on every task. Non-negotiable principles that govern
|
|
5
|
-
all work: how to think, what to change, and how to verify.
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Core Principles
|
|
9
|
-
|
|
10
|
-
> Simplification is the ultimate sophistication.
|
|
11
|
-
> Write code that is simple, small, easy to delete, and adds nothing unnecessary.
|
|
12
|
-
|
|
13
|
-
1. **Think Before Coding** – Understand the goal before writing a line. If requirements are ambiguous, **stop and ask** — assumptions compound. Surface tradeoffs to the caller, not just in comments.
|
|
14
|
-
2. **Simplicity First** - Write the minimum code that correctly solves the problem. Avoid speculative features.
|
|
15
|
-
3. **Surgical Changes** – Touch only what the task requires. Avoid "while I'm here" changes. Refactoring is a separate, explicit task — never bundled into a feature or fix.
|
|
16
|
-
4. **Goal-Driven Testing** – Define success before writing code. Tests are the contract. Cover the failure path — a test suite that only passes on happy inputs is not a contract, it's a wish.
|
|
17
|
-
5. **No Race Conditions** - Treat shared mutable state as dangerous. Prefer immutability.
|
|
18
|
-
6. **Agent Memory** – Every task is a learning loop. Read `AgentSkills/memory/index.md` at task start, then load only the domain file(s) matching your task. Before closing, check if a new lesson is warranted — if so, read `AgentSkills/memory/schema.md`, append one line to the correct domain file, and update the count in `index.md`.
|
|
19
|
-
|
|
20
|
-
7. **Fail Loudly, Recover Gracefully** – Errors are a first-class output. When something goes wrong, fail loudly with context (what, where, relevant IDs). Never swallow exceptions silently. Recovery paths must be as deliberate as the happy path.
|
|
21
|
-
|
|
22
|
-
8. **Prefer Reversible Moves** – When two approaches solve the problem equally well, choose the one that can be undone. Flag irreversible changes (schema drops, destructive migrations, hard deletes) explicitly before executing them.
|
|
23
|
-
|
|
1
|
+
---
|
|
2
|
+
name: core
|
|
3
|
+
description: >
|
|
4
|
+
Load on every task. Non-negotiable principles that govern
|
|
5
|
+
all work: how to think, what to change, and how to verify.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Core Principles
|
|
9
|
+
|
|
10
|
+
> Simplification is the ultimate sophistication.
|
|
11
|
+
> Write code that is simple, small, easy to delete, and adds nothing unnecessary.
|
|
12
|
+
|
|
13
|
+
1. **Think Before Coding** – Understand the goal before writing a line. If requirements are ambiguous, **stop and ask** — assumptions compound. Surface tradeoffs to the caller, not just in comments.
|
|
14
|
+
2. **Simplicity First** - Write the minimum code that correctly solves the problem. Avoid speculative features.
|
|
15
|
+
3. **Surgical Changes** – Touch only what the task requires. Avoid "while I'm here" changes. Refactoring is a separate, explicit task — never bundled into a feature or fix.
|
|
16
|
+
4. **Goal-Driven Testing** – Define success before writing code. Tests are the contract. Cover the failure path — a test suite that only passes on happy inputs is not a contract, it's a wish.
|
|
17
|
+
5. **No Race Conditions** - Treat shared mutable state as dangerous. Prefer immutability.
|
|
18
|
+
6. **Agent Memory** – Every task is a learning loop. Read `AgentSkills/memory/index.md` at task start, then load only the domain file(s) matching your task. Before closing, check if a new lesson is warranted — if so, read `AgentSkills/memory/schema.md`, append one line to the correct domain file, and update the count in `index.md`.
|
|
19
|
+
|
|
20
|
+
7. **Fail Loudly, Recover Gracefully** – Errors are a first-class output. When something goes wrong, fail loudly with context (what, where, relevant IDs). Never swallow exceptions silently. Recovery paths must be as deliberate as the happy path.
|
|
21
|
+
|
|
22
|
+
8. **Prefer Reversible Moves** – When two approaches solve the problem equally well, choose the one that can be undone. Flag irreversible changes (schema drops, destructive migrations, hard deletes) explicitly before executing them.
|
|
23
|
+
|
|
24
24
|
## Prerequisite Gate
|
|
25
25
|
|
|
26
26
|
Every task starts with the shared operating contract in
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: csharp-xunit
|
|
3
|
-
description: 'Get best practices for XUnit unit testing, including data-driven tests'
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# XUnit Best Practices
|
|
7
|
-
|
|
8
|
-
Your goal is to help me write effective unit tests with XUnit, covering both standard and data-driven testing approaches.
|
|
9
|
-
|
|
10
|
-
## Project Setup
|
|
11
|
-
|
|
12
|
-
- Use a separate test project with naming convention `[ProjectName].Tests`
|
|
13
|
-
- Reference Microsoft.NET.Test.Sdk, xunit, and xunit.runner.visualstudio packages
|
|
14
|
-
- Create test classes that match the classes being tested (e.g., `CalculatorTests` for `Calculator`)
|
|
15
|
-
- Use .NET SDK test commands: `dotnet test` for running tests
|
|
16
|
-
|
|
17
|
-
## Test Structure
|
|
18
|
-
|
|
19
|
-
- No test class attributes required (unlike MSTest/NUnit)
|
|
20
|
-
- Use fact-based tests with `[Fact]` attribute for simple tests
|
|
21
|
-
- Follow the Arrange-Act-Assert (AAA) pattern
|
|
22
|
-
- Name tests using the pattern `MethodName_Scenario_ExpectedBehavior`
|
|
23
|
-
- Use constructor for setup and `IDisposable.Dispose()` for teardown
|
|
24
|
-
- Use `IClassFixture<T>` for shared context between tests in a class
|
|
25
|
-
- Use `ICollectionFixture<T>` for shared context between multiple test classes
|
|
26
|
-
|
|
27
|
-
## Standard Tests
|
|
28
|
-
|
|
29
|
-
- Keep tests focused on a single behavior
|
|
30
|
-
- Avoid testing multiple behaviors in one test method
|
|
31
|
-
- Use clear assertions that express intent
|
|
32
|
-
- Include only the assertions needed to verify the test case
|
|
33
|
-
- Make tests independent and idempotent (can run in any order)
|
|
34
|
-
- Avoid test interdependencies
|
|
35
|
-
|
|
36
|
-
## Data-Driven Tests
|
|
37
|
-
|
|
38
|
-
- Use `[Theory]` combined with data source attributes
|
|
39
|
-
- Use `[InlineData]` for inline test data
|
|
40
|
-
- Use `[MemberData]` for method-based test data
|
|
41
|
-
- Use `[ClassData]` for class-based test data
|
|
42
|
-
- Create custom data attributes by implementing `DataAttribute`
|
|
43
|
-
- Use meaningful parameter names in data-driven tests
|
|
44
|
-
|
|
45
|
-
## Assertions
|
|
46
|
-
|
|
47
|
-
- Use `Assert.Equal` for value equality
|
|
48
|
-
- Use `Assert.Same` for reference equality
|
|
49
|
-
- Use `Assert.True`/`Assert.False` for boolean conditions
|
|
50
|
-
- Use `Assert.Contains`/`Assert.DoesNotContain` for collections
|
|
51
|
-
- Use `Assert.Matches`/`Assert.DoesNotMatch` for regex pattern matching
|
|
52
|
-
- Use `Assert.Throws<T>` or `await Assert.ThrowsAsync<T>` to test exceptions
|
|
53
|
-
- Use fluent assertions library for more readable assertions
|
|
54
|
-
|
|
55
|
-
## Mocking and Isolation
|
|
56
|
-
|
|
57
|
-
- Consider using Moq or NSubstitute alongside XUnit
|
|
58
|
-
- Mock dependencies to isolate units under test
|
|
59
|
-
- Use interfaces to facilitate mocking
|
|
60
|
-
- Consider using a DI container for complex test setups
|
|
61
|
-
|
|
62
|
-
## Test Organization
|
|
63
|
-
|
|
64
|
-
- Group tests by feature or component
|
|
65
|
-
- Use `[Trait("Category", "CategoryName")]` for categorization
|
|
66
|
-
- Use collection fixtures to group tests with shared dependencies
|
|
67
|
-
- Consider output helpers (`ITestOutputHelper`) for test diagnostics
|
|
1
|
+
---
|
|
2
|
+
name: csharp-xunit
|
|
3
|
+
description: 'Get best practices for XUnit unit testing, including data-driven tests'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# XUnit Best Practices
|
|
7
|
+
|
|
8
|
+
Your goal is to help me write effective unit tests with XUnit, covering both standard and data-driven testing approaches.
|
|
9
|
+
|
|
10
|
+
## Project Setup
|
|
11
|
+
|
|
12
|
+
- Use a separate test project with naming convention `[ProjectName].Tests`
|
|
13
|
+
- Reference Microsoft.NET.Test.Sdk, xunit, and xunit.runner.visualstudio packages
|
|
14
|
+
- Create test classes that match the classes being tested (e.g., `CalculatorTests` for `Calculator`)
|
|
15
|
+
- Use .NET SDK test commands: `dotnet test` for running tests
|
|
16
|
+
|
|
17
|
+
## Test Structure
|
|
18
|
+
|
|
19
|
+
- No test class attributes required (unlike MSTest/NUnit)
|
|
20
|
+
- Use fact-based tests with `[Fact]` attribute for simple tests
|
|
21
|
+
- Follow the Arrange-Act-Assert (AAA) pattern
|
|
22
|
+
- Name tests using the pattern `MethodName_Scenario_ExpectedBehavior`
|
|
23
|
+
- Use constructor for setup and `IDisposable.Dispose()` for teardown
|
|
24
|
+
- Use `IClassFixture<T>` for shared context between tests in a class
|
|
25
|
+
- Use `ICollectionFixture<T>` for shared context between multiple test classes
|
|
26
|
+
|
|
27
|
+
## Standard Tests
|
|
28
|
+
|
|
29
|
+
- Keep tests focused on a single behavior
|
|
30
|
+
- Avoid testing multiple behaviors in one test method
|
|
31
|
+
- Use clear assertions that express intent
|
|
32
|
+
- Include only the assertions needed to verify the test case
|
|
33
|
+
- Make tests independent and idempotent (can run in any order)
|
|
34
|
+
- Avoid test interdependencies
|
|
35
|
+
|
|
36
|
+
## Data-Driven Tests
|
|
37
|
+
|
|
38
|
+
- Use `[Theory]` combined with data source attributes
|
|
39
|
+
- Use `[InlineData]` for inline test data
|
|
40
|
+
- Use `[MemberData]` for method-based test data
|
|
41
|
+
- Use `[ClassData]` for class-based test data
|
|
42
|
+
- Create custom data attributes by implementing `DataAttribute`
|
|
43
|
+
- Use meaningful parameter names in data-driven tests
|
|
44
|
+
|
|
45
|
+
## Assertions
|
|
46
|
+
|
|
47
|
+
- Use `Assert.Equal` for value equality
|
|
48
|
+
- Use `Assert.Same` for reference equality
|
|
49
|
+
- Use `Assert.True`/`Assert.False` for boolean conditions
|
|
50
|
+
- Use `Assert.Contains`/`Assert.DoesNotContain` for collections
|
|
51
|
+
- Use `Assert.Matches`/`Assert.DoesNotMatch` for regex pattern matching
|
|
52
|
+
- Use `Assert.Throws<T>` or `await Assert.ThrowsAsync<T>` to test exceptions
|
|
53
|
+
- Use fluent assertions library for more readable assertions
|
|
54
|
+
|
|
55
|
+
## Mocking and Isolation
|
|
56
|
+
|
|
57
|
+
- Consider using Moq or NSubstitute alongside XUnit
|
|
58
|
+
- Mock dependencies to isolate units under test
|
|
59
|
+
- Use interfaces to facilitate mocking
|
|
60
|
+
- Consider using a DI container for complex test setups
|
|
61
|
+
|
|
62
|
+
## Test Organization
|
|
63
|
+
|
|
64
|
+
- Group tests by feature or component
|
|
65
|
+
- Use `[Trait("Category", "CategoryName")]` for categorization
|
|
66
|
+
- Use collection fixtures to group tests with shared dependencies
|
|
67
|
+
- Consider output helpers (`ITestOutputHelper`) for test diagnostics
|
|
68
68
|
- Skip tests conditionally with `Skip = "reason"` in fact/theory attributes
|
|
@@ -1,108 +1,108 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: design
|
|
3
|
-
description: >
|
|
4
|
-
Load when designing, architecting, or implementing patterns in C#.
|
|
5
|
-
Covers project folder structure (vertical slice), SOLID, KISS, YAGNI,
|
|
6
|
-
DRY, composition, Singleton, and Factory.
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Design Principles and Patterns
|
|
10
|
-
|
|
11
|
-
## Project Structure
|
|
12
|
-
|
|
13
|
-
Organise by **feature (vertical slice)**, not by technical layer.
|
|
14
|
-
Each feature folder owns everything needed for its use case: command, handler, DTO, validator, and endpoint.
|
|
15
|
-
This keeps changes local, avoids cross-cutting layers, and scales without requiring global refactors.
|
|
16
|
-
|
|
17
|
-
```text
|
|
18
|
-
src/
|
|
19
|
-
├── Features/
|
|
20
|
-
│ ├── Products/
|
|
21
|
-
│ │ ├── CreateProductCommand.cs
|
|
22
|
-
│ │ ├── CreateProductHandler.cs
|
|
23
|
-
│ │ ├── ProductDto.cs
|
|
24
|
-
│ │ ├── ProductValidator.cs
|
|
25
|
-
│ │ └── ProductEndpoints.cs
|
|
26
|
-
│ ├── Orders/
|
|
27
|
-
│ │ ├── CreateOrderCommand.cs
|
|
28
|
-
│ │ ├── CreateOrderHandler.cs
|
|
29
|
-
│ │ ├── OrderDto.cs
|
|
30
|
-
│ │ ├── OrderValidator.cs
|
|
31
|
-
│ │ └── OrderEndpoints.cs
|
|
32
|
-
│ └── ...
|
|
33
|
-
├── Shared/
|
|
34
|
-
│ ├── Middleware/
|
|
35
|
-
│ └── Utilities/
|
|
36
|
-
└── Program.cs
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
**Rules:**
|
|
40
|
-
- One folder per feature — never split a feature across `Commands/`, `Queries/`, `Controllers/` top-level layers.
|
|
41
|
-
- When a feature grows, split it into sub-features within the same `Features/<Feature>/` directory before creating a new top-level folder.
|
|
42
|
-
- Shared cross-cutting code (middleware, utilities, base types) lives in `Shared/`, not duplicated per feature.
|
|
43
|
-
- `Program.cs` wires up DI, middleware, and endpoint registration only — no business logic.
|
|
44
|
-
|
|
45
|
-
## SOLID
|
|
46
|
-
|
|
47
|
-
| | Principle | In Practice |
|
|
48
|
-
|---|---|---|
|
|
49
|
-
| S | Single Responsibility | One class, one reason to change |
|
|
50
|
-
| O | Open / Closed | Open to extension, closed to modification |
|
|
51
|
-
| L | Liskov Substitution | Subtypes must work wherever the base type works |
|
|
52
|
-
| I | Interface Segregation | Small, focused interfaces over large, general ones |
|
|
53
|
-
| D | Dependency Inversion | Depend on abstractions, not concrete implementations |
|
|
54
|
-
|
|
55
|
-
## KISS
|
|
56
|
-
|
|
57
|
-
Prefer the obvious solution. If a junior engineer cannot understand it in 5 minutes, simplify it.
|
|
58
|
-
|
|
59
|
-
## YAGNI
|
|
60
|
-
|
|
61
|
-
Do not build something until it is needed. Solve today's requirement, not tomorrow's guess.
|
|
62
|
-
|
|
63
|
-
```csharp
|
|
64
|
-
// Bad: built "just in case" when only one format exists.
|
|
65
|
-
public interface IExporter { }
|
|
66
|
-
public class ExporterFactory { }
|
|
67
|
-
|
|
68
|
-
// Good: solve what exists now.
|
|
69
|
-
public string ExportToCsv(Report report) { }
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## DRY
|
|
73
|
-
|
|
74
|
-
Keep one source of truth per piece of knowledge: logic, configuration, and data schemas.
|
|
75
|
-
|
|
76
|
-
## Composition Over Inheritance
|
|
77
|
-
|
|
78
|
-
Use inheritance only for true "is-a" relationships. Prefer interfaces and delegation.
|
|
79
|
-
|
|
80
|
-
## Pattern: Singleton
|
|
81
|
-
|
|
82
|
-
Use when exactly one instance must coordinate shared state. Avoid when it creates hidden global state.
|
|
83
|
-
|
|
84
|
-
```csharp
|
|
85
|
-
public sealed class AppConfig
|
|
86
|
-
{
|
|
87
|
-
private static readonly Lazy<AppConfig> InstanceHolder =
|
|
88
|
-
new(() => new AppConfig());
|
|
89
|
-
|
|
90
|
-
public static AppConfig Instance => InstanceHolder.Value;
|
|
91
|
-
|
|
92
|
-
private AppConfig()
|
|
93
|
-
{
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Pattern: Factory
|
|
99
|
-
|
|
100
|
-
Use when creation is complex, varies by type, or must be decoupled from consumers.
|
|
101
|
-
|
|
102
|
-
```csharp
|
|
103
|
-
// Bad
|
|
104
|
-
var handler = type == "email" ? new EmailHandler() : new SmsHandler();
|
|
105
|
-
|
|
106
|
-
// Good
|
|
107
|
-
var handler = NotificationFactory.Create(type);
|
|
108
|
-
```
|
|
1
|
+
---
|
|
2
|
+
name: design
|
|
3
|
+
description: >
|
|
4
|
+
Load when designing, architecting, or implementing patterns in C#.
|
|
5
|
+
Covers project folder structure (vertical slice), SOLID, KISS, YAGNI,
|
|
6
|
+
DRY, composition, Singleton, and Factory.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Design Principles and Patterns
|
|
10
|
+
|
|
11
|
+
## Project Structure
|
|
12
|
+
|
|
13
|
+
Organise by **feature (vertical slice)**, not by technical layer.
|
|
14
|
+
Each feature folder owns everything needed for its use case: command, handler, DTO, validator, and endpoint.
|
|
15
|
+
This keeps changes local, avoids cross-cutting layers, and scales without requiring global refactors.
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
src/
|
|
19
|
+
├── Features/
|
|
20
|
+
│ ├── Products/
|
|
21
|
+
│ │ ├── CreateProductCommand.cs
|
|
22
|
+
│ │ ├── CreateProductHandler.cs
|
|
23
|
+
│ │ ├── ProductDto.cs
|
|
24
|
+
│ │ ├── ProductValidator.cs
|
|
25
|
+
│ │ └── ProductEndpoints.cs
|
|
26
|
+
│ ├── Orders/
|
|
27
|
+
│ │ ├── CreateOrderCommand.cs
|
|
28
|
+
│ │ ├── CreateOrderHandler.cs
|
|
29
|
+
│ │ ├── OrderDto.cs
|
|
30
|
+
│ │ ├── OrderValidator.cs
|
|
31
|
+
│ │ └── OrderEndpoints.cs
|
|
32
|
+
│ └── ...
|
|
33
|
+
├── Shared/
|
|
34
|
+
│ ├── Middleware/
|
|
35
|
+
│ └── Utilities/
|
|
36
|
+
└── Program.cs
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Rules:**
|
|
40
|
+
- One folder per feature — never split a feature across `Commands/`, `Queries/`, `Controllers/` top-level layers.
|
|
41
|
+
- When a feature grows, split it into sub-features within the same `Features/<Feature>/` directory before creating a new top-level folder.
|
|
42
|
+
- Shared cross-cutting code (middleware, utilities, base types) lives in `Shared/`, not duplicated per feature.
|
|
43
|
+
- `Program.cs` wires up DI, middleware, and endpoint registration only — no business logic.
|
|
44
|
+
|
|
45
|
+
## SOLID
|
|
46
|
+
|
|
47
|
+
| | Principle | In Practice |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| S | Single Responsibility | One class, one reason to change |
|
|
50
|
+
| O | Open / Closed | Open to extension, closed to modification |
|
|
51
|
+
| L | Liskov Substitution | Subtypes must work wherever the base type works |
|
|
52
|
+
| I | Interface Segregation | Small, focused interfaces over large, general ones |
|
|
53
|
+
| D | Dependency Inversion | Depend on abstractions, not concrete implementations |
|
|
54
|
+
|
|
55
|
+
## KISS
|
|
56
|
+
|
|
57
|
+
Prefer the obvious solution. If a junior engineer cannot understand it in 5 minutes, simplify it.
|
|
58
|
+
|
|
59
|
+
## YAGNI
|
|
60
|
+
|
|
61
|
+
Do not build something until it is needed. Solve today's requirement, not tomorrow's guess.
|
|
62
|
+
|
|
63
|
+
```csharp
|
|
64
|
+
// Bad: built "just in case" when only one format exists.
|
|
65
|
+
public interface IExporter { }
|
|
66
|
+
public class ExporterFactory { }
|
|
67
|
+
|
|
68
|
+
// Good: solve what exists now.
|
|
69
|
+
public string ExportToCsv(Report report) { }
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## DRY
|
|
73
|
+
|
|
74
|
+
Keep one source of truth per piece of knowledge: logic, configuration, and data schemas.
|
|
75
|
+
|
|
76
|
+
## Composition Over Inheritance
|
|
77
|
+
|
|
78
|
+
Use inheritance only for true "is-a" relationships. Prefer interfaces and delegation.
|
|
79
|
+
|
|
80
|
+
## Pattern: Singleton
|
|
81
|
+
|
|
82
|
+
Use when exactly one instance must coordinate shared state. Avoid when it creates hidden global state.
|
|
83
|
+
|
|
84
|
+
```csharp
|
|
85
|
+
public sealed class AppConfig
|
|
86
|
+
{
|
|
87
|
+
private static readonly Lazy<AppConfig> InstanceHolder =
|
|
88
|
+
new(() => new AppConfig());
|
|
89
|
+
|
|
90
|
+
public static AppConfig Instance => InstanceHolder.Value;
|
|
91
|
+
|
|
92
|
+
private AppConfig()
|
|
93
|
+
{
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Pattern: Factory
|
|
99
|
+
|
|
100
|
+
Use when creation is complex, varies by type, or must be decoupled from consumers.
|
|
101
|
+
|
|
102
|
+
```csharp
|
|
103
|
+
// Bad
|
|
104
|
+
var handler = type == "email" ? new EmailHandler() : new SmsHandler();
|
|
105
|
+
|
|
106
|
+
// Good
|
|
107
|
+
var handler = NotificationFactory.Create(type);
|
|
108
|
+
```
|