@atlashub/smartstack-cli 1.36.0 → 2.0.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/config/mcp-defaults.json +62 -0
- package/dist/index.js +57 -4
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +16984 -0
- package/dist/mcp-entry.mjs.map +1 -0
- package/package.json +14 -5
- package/templates/agents/gitflow/start.md +5 -4
- package/templates/agents/mcp-healthcheck.md +15 -13
- package/templates/mcp-scaffolding/component.tsx.hbs +298 -0
- package/templates/mcp-scaffolding/controller.cs.hbs +184 -0
- package/templates/mcp-scaffolding/entity-extension.cs.hbs +231 -0
- package/templates/mcp-scaffolding/frontend/api-client.ts.hbs +116 -0
- package/templates/mcp-scaffolding/frontend/nav-routes.ts.hbs +133 -0
- package/templates/mcp-scaffolding/frontend/routes.tsx.hbs +134 -0
- package/templates/mcp-scaffolding/migrations/seed-roles.cs.hbs +261 -0
- package/templates/mcp-scaffolding/service-extension.cs.hbs +53 -0
- package/templates/mcp-scaffolding/tests/controller.test.cs.hbs +413 -0
- package/templates/mcp-scaffolding/tests/entity.test.cs.hbs +239 -0
- package/templates/mcp-scaffolding/tests/repository.test.cs.hbs +441 -0
- package/templates/mcp-scaffolding/tests/security.test.cs.hbs +442 -0
- package/templates/mcp-scaffolding/tests/service.test.cs.hbs +390 -0
- package/templates/mcp-scaffolding/tests/validator.test.cs.hbs +428 -0
- package/templates/ralph/README.md +3 -3
- package/templates/ralph/ralph.config.yaml +2 -2
- package/templates/skills/admin/SKILL.md +42 -0
- package/templates/skills/application/steps/step-01-navigation.md +226 -43
- package/templates/skills/application/steps/step-03-roles.md +160 -38
- package/templates/skills/application/steps/step-04-backend.md +109 -2
- package/templates/skills/application/templates-seed.md +200 -1
- package/templates/skills/business-analyse/_shared.md +24 -1
- package/templates/skills/business-analyse/questionnaire/01-context.md +4 -4
- package/templates/skills/business-analyse/questionnaire/02-stakeholders.md +3 -3
- package/templates/skills/business-analyse/questionnaire/03-scope.md +4 -4
- package/templates/skills/business-analyse/questionnaire/04-data.md +7 -7
- package/templates/skills/business-analyse/questionnaire/05-integrations.md +1 -1
- package/templates/skills/business-analyse/questionnaire/06-security.md +3 -3
- package/templates/skills/business-analyse/questionnaire/07-ui.md +1 -1
- package/templates/skills/business-analyse/questionnaire/08-performance.md +3 -3
- package/templates/skills/business-analyse/questionnaire/09-constraints.md +4 -4
- package/templates/skills/business-analyse/questionnaire/10-documentation.md +2 -2
- package/templates/skills/business-analyse/questionnaire/11-data-lifecycle.md +2 -2
- package/templates/skills/business-analyse/questionnaire/12-migration.md +1 -1
- package/templates/skills/business-analyse/questionnaire/13-cross-module.md +2 -2
- package/templates/skills/business-analyse/steps/step-01-discover.md +50 -25
- package/templates/skills/business-analyse/steps/step-05-handoff.md +133 -34
- package/templates/skills/cc-agent/SKILL.md +129 -0
- package/templates/skills/cc-agent/references/agent-frontmatter.md +213 -0
- package/templates/skills/cc-agent/references/permission-modes.md +102 -0
- package/templates/skills/cc-agent/references/tools-reference.md +144 -0
- package/templates/skills/cc-agent/steps/step-00-init.md +134 -0
- package/templates/skills/cc-agent/steps/step-01-design.md +186 -0
- package/templates/skills/cc-agent/steps/step-02-generate.md +204 -0
- package/templates/skills/cc-agent/steps/step-03-validate.md +130 -0
- package/templates/skills/cc-agent/templates/agent-categorized.md +67 -0
- package/templates/skills/cc-agent/templates/agent-standalone.md +56 -0
- package/templates/skills/cc-agent/templates/agent-with-skills.md +94 -0
- package/templates/skills/cc-audit/SKILL.md +108 -0
- package/templates/skills/cc-audit/references/agent-checklist.md +91 -0
- package/templates/skills/cc-audit/references/hook-checklist.md +110 -0
- package/templates/skills/cc-audit/references/skill-checklist.md +70 -0
- package/templates/skills/cc-audit/steps/step-00-init.md +98 -0
- package/templates/skills/cc-audit/steps/step-01-scan.md +142 -0
- package/templates/skills/cc-audit/steps/step-02-analyze.md +158 -0
- package/templates/skills/cc-audit/steps/step-03-report.md +142 -0
- package/templates/skills/cc-skill/SKILL.md +134 -0
- package/templates/skills/cc-skill/references/best-practices.md +167 -0
- package/templates/skills/cc-skill/references/frontmatter-reference.md +182 -0
- package/templates/skills/cc-skill/references/skill-patterns.md +199 -0
- package/templates/skills/cc-skill/steps/step-00-init.md +119 -0
- package/templates/skills/cc-skill/steps/step-01-design.md +199 -0
- package/templates/skills/cc-skill/steps/step-02-generate.md +145 -0
- package/templates/skills/cc-skill/steps/step-03-steps.md +151 -0
- package/templates/skills/cc-skill/steps/step-04-validate.md +124 -0
- package/templates/skills/cc-skill/templates/skill-forked.md +85 -0
- package/templates/skills/cc-skill/templates/skill-progressive.md +102 -0
- package/templates/skills/cc-skill/templates/skill-simple.md +75 -0
- package/templates/skills/cc-skill/templates/step-template.md +82 -0
- package/templates/skills/check-version/SKILL.md +6 -0
- package/templates/skills/debug/SKILL.md +4 -0
- package/templates/skills/documentation/SKILL.md +1 -0
- package/templates/skills/efcore/SKILL.md +5 -0
- package/templates/skills/efcore/steps/db/step-deploy.md +26 -5
- package/templates/skills/efcore/steps/shared/step-00-init.md +21 -7
- package/templates/skills/explore/SKILL.md +28 -32
- package/templates/skills/feature-full/SKILL.md +1 -0
- package/templates/skills/gitflow/SKILL.md +8 -0
- package/templates/skills/gitflow/steps/step-start.md +45 -10
- package/templates/skills/mcp/SKILL.md +38 -18
- package/templates/skills/quick-search/SKILL.md +8 -1
- package/templates/skills/ralph-loop/SKILL.md +1 -1
- package/templates/skills/ralph-loop/steps/step-00-init.md +8 -68
- package/templates/skills/ralph-loop/steps/step-04-check.md +1 -1
- package/templates/skills/refactor/SKILL.md +1 -0
- package/templates/skills/review-code/SKILL.md +7 -1
- package/templates/skills/ui-components/SKILL.md +31 -438
- package/templates/skills/ui-components/accessibility.md +170 -0
- package/templates/skills/ui-components/patterns/data-table.md +39 -0
- package/templates/skills/ui-components/patterns/entity-card.md +77 -0
- package/templates/skills/ui-components/patterns/grid-layout.md +91 -0
- package/templates/skills/ui-components/patterns/kanban.md +43 -0
- package/templates/skills/ui-components/style-guide.md +86 -0
- package/templates/skills/utils/SKILL.md +1 -0
- package/templates/skills/validate/SKILL.md +1 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
{{!-- SmartStack Validator Test Template --}}
|
|
2
|
+
{{!-- Generates unit tests for FluentValidation validators following SmartStack conventions --}}
|
|
3
|
+
|
|
4
|
+
using FluentAssertions;
|
|
5
|
+
using FluentValidation.TestHelper;
|
|
6
|
+
using Xunit;
|
|
7
|
+
using {{namespace}}.Application.DTOs;
|
|
8
|
+
using {{namespace}}.Application.Validators;
|
|
9
|
+
|
|
10
|
+
namespace {{namespace}}.Tests.Unit.Validators;
|
|
11
|
+
|
|
12
|
+
/// <summary>
|
|
13
|
+
/// Unit tests for <see cref="{{name}}Validator"/>.
|
|
14
|
+
/// Follows SmartStack testing conventions: {Method}_When{Condition}_Should{Result}
|
|
15
|
+
/// </summary>
|
|
16
|
+
public class {{name}}ValidatorTests
|
|
17
|
+
{
|
|
18
|
+
private readonly Create{{name}}RequestValidator _createValidator;
|
|
19
|
+
private readonly Update{{name}}RequestValidator _updateValidator;
|
|
20
|
+
|
|
21
|
+
public {{name}}ValidatorTests()
|
|
22
|
+
{
|
|
23
|
+
_createValidator = new Create{{name}}RequestValidator();
|
|
24
|
+
_updateValidator = new Update{{name}}RequestValidator();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
#region Create Request - Code Validation
|
|
28
|
+
|
|
29
|
+
[Fact]
|
|
30
|
+
public void CreateValidator_WhenCodeIsValid_ShouldNotHaveErrors()
|
|
31
|
+
{
|
|
32
|
+
// Arrange
|
|
33
|
+
var request = new Create{{name}}Request { Code = "VALID-001" };
|
|
34
|
+
|
|
35
|
+
// Act
|
|
36
|
+
var result = _createValidator.TestValidate(request);
|
|
37
|
+
|
|
38
|
+
// Assert
|
|
39
|
+
result.ShouldNotHaveValidationErrorFor(x => x.Code);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
[Theory]
|
|
43
|
+
[InlineData(null)]
|
|
44
|
+
[InlineData("")]
|
|
45
|
+
[InlineData(" ")]
|
|
46
|
+
public void CreateValidator_WhenCodeIsNullOrEmpty_ShouldHaveError(string? invalidCode)
|
|
47
|
+
{
|
|
48
|
+
// Arrange
|
|
49
|
+
var request = new Create{{name}}Request { Code = invalidCode! };
|
|
50
|
+
|
|
51
|
+
// Act
|
|
52
|
+
var result = _createValidator.TestValidate(request);
|
|
53
|
+
|
|
54
|
+
// Assert
|
|
55
|
+
result.ShouldHaveValidationErrorFor(x => x.Code)
|
|
56
|
+
.WithErrorMessage("*required*");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[Fact]
|
|
60
|
+
public void CreateValidator_WhenCodeExceedsMaxLength_ShouldHaveError()
|
|
61
|
+
{
|
|
62
|
+
// Arrange
|
|
63
|
+
var request = new Create{{name}}Request
|
|
64
|
+
{
|
|
65
|
+
Code = new string('A', 101) // Exceeds 100 char limit
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Act
|
|
69
|
+
var result = _createValidator.TestValidate(request);
|
|
70
|
+
|
|
71
|
+
// Assert
|
|
72
|
+
result.ShouldHaveValidationErrorFor(x => x.Code)
|
|
73
|
+
.WithErrorMessage("*100 characters*");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
[Theory]
|
|
77
|
+
[InlineData("valid-code")]
|
|
78
|
+
[InlineData("VALID_CODE_123")]
|
|
79
|
+
[InlineData("Code.With.Dots")]
|
|
80
|
+
public void CreateValidator_WhenCodeHasValidFormat_ShouldNotHaveErrors(string validCode)
|
|
81
|
+
{
|
|
82
|
+
// Arrange
|
|
83
|
+
var request = new Create{{name}}Request { Code = validCode };
|
|
84
|
+
|
|
85
|
+
// Act
|
|
86
|
+
var result = _createValidator.TestValidate(request);
|
|
87
|
+
|
|
88
|
+
// Assert
|
|
89
|
+
result.ShouldNotHaveValidationErrorFor(x => x.Code);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
[Theory]
|
|
93
|
+
[InlineData("code with spaces")]
|
|
94
|
+
[InlineData("code<script>")]
|
|
95
|
+
[InlineData("code;drop")]
|
|
96
|
+
public void CreateValidator_WhenCodeHasInvalidCharacters_ShouldHaveError(string invalidCode)
|
|
97
|
+
{
|
|
98
|
+
// Arrange
|
|
99
|
+
var request = new Create{{name}}Request { Code = invalidCode };
|
|
100
|
+
|
|
101
|
+
// Act
|
|
102
|
+
var result = _createValidator.TestValidate(request);
|
|
103
|
+
|
|
104
|
+
// Assert
|
|
105
|
+
result.ShouldHaveValidationErrorFor(x => x.Code)
|
|
106
|
+
.WithErrorMessage("*invalid characters*");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
#endregion
|
|
110
|
+
|
|
111
|
+
#region Create Request - Name Validation
|
|
112
|
+
|
|
113
|
+
[Fact]
|
|
114
|
+
public void CreateValidator_WhenNameIsValid_ShouldNotHaveErrors()
|
|
115
|
+
{
|
|
116
|
+
// Arrange
|
|
117
|
+
var request = new Create{{name}}Request
|
|
118
|
+
{
|
|
119
|
+
Code = "TEST",
|
|
120
|
+
Name = "Valid Name"
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Act
|
|
124
|
+
var result = _createValidator.TestValidate(request);
|
|
125
|
+
|
|
126
|
+
// Assert
|
|
127
|
+
result.ShouldNotHaveValidationErrorFor(x => x.Name);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
[Fact]
|
|
131
|
+
public void CreateValidator_WhenNameExceedsMaxLength_ShouldHaveError()
|
|
132
|
+
{
|
|
133
|
+
// Arrange
|
|
134
|
+
var request = new Create{{name}}Request
|
|
135
|
+
{
|
|
136
|
+
Code = "TEST",
|
|
137
|
+
Name = new string('A', 256) // Exceeds 255 char limit
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// Act
|
|
141
|
+
var result = _createValidator.TestValidate(request);
|
|
142
|
+
|
|
143
|
+
// Assert
|
|
144
|
+
result.ShouldHaveValidationErrorFor(x => x.Name)
|
|
145
|
+
.WithErrorMessage("*255 characters*");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#endregion
|
|
149
|
+
|
|
150
|
+
#region Update Request Validation
|
|
151
|
+
|
|
152
|
+
[Fact]
|
|
153
|
+
public void UpdateValidator_WhenValidData_ShouldNotHaveErrors()
|
|
154
|
+
{
|
|
155
|
+
// Arrange
|
|
156
|
+
var request = new Update{{name}}Request
|
|
157
|
+
{
|
|
158
|
+
Name = "Updated Name",
|
|
159
|
+
// Add other updatable properties
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// Act
|
|
163
|
+
var result = _updateValidator.TestValidate(request);
|
|
164
|
+
|
|
165
|
+
// Assert
|
|
166
|
+
result.ShouldNotHaveAnyValidationErrors();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
[Fact]
|
|
170
|
+
public void UpdateValidator_WhenOptionalFieldsAreNull_ShouldNotHaveErrors()
|
|
171
|
+
{
|
|
172
|
+
// Arrange
|
|
173
|
+
var request = new Update{{name}}Request
|
|
174
|
+
{
|
|
175
|
+
Name = null, // Optional field can be null
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// Act
|
|
179
|
+
var result = _updateValidator.TestValidate(request);
|
|
180
|
+
|
|
181
|
+
// Assert
|
|
182
|
+
result.ShouldNotHaveAnyValidationErrors();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
#endregion
|
|
186
|
+
|
|
187
|
+
#region Date Validation (if applicable)
|
|
188
|
+
|
|
189
|
+
[Fact]
|
|
190
|
+
public void CreateValidator_WhenStartDateIsInPast_ShouldHaveError()
|
|
191
|
+
{
|
|
192
|
+
// Arrange
|
|
193
|
+
var request = new Create{{name}}Request
|
|
194
|
+
{
|
|
195
|
+
Code = "TEST",
|
|
196
|
+
StartDate = DateTime.UtcNow.AddDays(-1)
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Act
|
|
200
|
+
var result = _createValidator.TestValidate(request);
|
|
201
|
+
|
|
202
|
+
// Assert
|
|
203
|
+
result.ShouldHaveValidationErrorFor(x => x.StartDate)
|
|
204
|
+
.WithErrorMessage("*future*");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
[Fact]
|
|
208
|
+
public void CreateValidator_WhenEndDateBeforeStartDate_ShouldHaveError()
|
|
209
|
+
{
|
|
210
|
+
// Arrange
|
|
211
|
+
var request = new Create{{name}}Request
|
|
212
|
+
{
|
|
213
|
+
Code = "TEST",
|
|
214
|
+
StartDate = DateTime.UtcNow.AddDays(10),
|
|
215
|
+
EndDate = DateTime.UtcNow.AddDays(5)
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// Act
|
|
219
|
+
var result = _createValidator.TestValidate(request);
|
|
220
|
+
|
|
221
|
+
// Assert
|
|
222
|
+
result.ShouldHaveValidationErrorFor(x => x.EndDate)
|
|
223
|
+
.WithErrorMessage("*after start date*");
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
#endregion
|
|
227
|
+
|
|
228
|
+
#region Numeric Validation (if applicable)
|
|
229
|
+
|
|
230
|
+
[Theory]
|
|
231
|
+
[InlineData(-1)]
|
|
232
|
+
[InlineData(-100)]
|
|
233
|
+
public void CreateValidator_WhenQuantityIsNegative_ShouldHaveError(int invalidQuantity)
|
|
234
|
+
{
|
|
235
|
+
// Arrange
|
|
236
|
+
var request = new Create{{name}}Request
|
|
237
|
+
{
|
|
238
|
+
Code = "TEST",
|
|
239
|
+
Quantity = invalidQuantity
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// Act
|
|
243
|
+
var result = _createValidator.TestValidate(request);
|
|
244
|
+
|
|
245
|
+
// Assert
|
|
246
|
+
result.ShouldHaveValidationErrorFor(x => x.Quantity)
|
|
247
|
+
.WithErrorMessage("*greater than*0*");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
[Theory]
|
|
251
|
+
[InlineData(0)]
|
|
252
|
+
[InlineData(1)]
|
|
253
|
+
[InlineData(1000)]
|
|
254
|
+
public void CreateValidator_WhenQuantityIsValid_ShouldNotHaveErrors(int validQuantity)
|
|
255
|
+
{
|
|
256
|
+
// Arrange
|
|
257
|
+
var request = new Create{{name}}Request
|
|
258
|
+
{
|
|
259
|
+
Code = "TEST",
|
|
260
|
+
Quantity = validQuantity
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// Act
|
|
264
|
+
var result = _createValidator.TestValidate(request);
|
|
265
|
+
|
|
266
|
+
// Assert
|
|
267
|
+
result.ShouldNotHaveValidationErrorFor(x => x.Quantity);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
#endregion
|
|
271
|
+
|
|
272
|
+
#region Email Validation (if applicable)
|
|
273
|
+
|
|
274
|
+
[Theory]
|
|
275
|
+
[InlineData("valid@example.com")]
|
|
276
|
+
[InlineData("user.name@domain.org")]
|
|
277
|
+
[InlineData("user+tag@example.co.uk")]
|
|
278
|
+
public void CreateValidator_WhenEmailIsValid_ShouldNotHaveErrors(string validEmail)
|
|
279
|
+
{
|
|
280
|
+
// Arrange
|
|
281
|
+
var request = new Create{{name}}Request
|
|
282
|
+
{
|
|
283
|
+
Code = "TEST",
|
|
284
|
+
Email = validEmail
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Act
|
|
288
|
+
var result = _createValidator.TestValidate(request);
|
|
289
|
+
|
|
290
|
+
// Assert
|
|
291
|
+
result.ShouldNotHaveValidationErrorFor(x => x.Email);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
[Theory]
|
|
295
|
+
[InlineData("invalid")]
|
|
296
|
+
[InlineData("invalid@")]
|
|
297
|
+
[InlineData("@invalid.com")]
|
|
298
|
+
[InlineData("invalid@.com")]
|
|
299
|
+
public void CreateValidator_WhenEmailIsInvalid_ShouldHaveError(string invalidEmail)
|
|
300
|
+
{
|
|
301
|
+
// Arrange
|
|
302
|
+
var request = new Create{{name}}Request
|
|
303
|
+
{
|
|
304
|
+
Code = "TEST",
|
|
305
|
+
Email = invalidEmail
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// Act
|
|
309
|
+
var result = _createValidator.TestValidate(request);
|
|
310
|
+
|
|
311
|
+
// Assert
|
|
312
|
+
result.ShouldHaveValidationErrorFor(x => x.Email)
|
|
313
|
+
.WithErrorMessage("*valid email*");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
#endregion
|
|
317
|
+
|
|
318
|
+
#region Collection Validation (if applicable)
|
|
319
|
+
|
|
320
|
+
[Fact]
|
|
321
|
+
public void CreateValidator_WhenItemsCollectionIsEmpty_ShouldHaveError()
|
|
322
|
+
{
|
|
323
|
+
// Arrange
|
|
324
|
+
var request = new Create{{name}}Request
|
|
325
|
+
{
|
|
326
|
+
Code = "TEST",
|
|
327
|
+
Items = new List<string>()
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// Act
|
|
331
|
+
var result = _createValidator.TestValidate(request);
|
|
332
|
+
|
|
333
|
+
// Assert
|
|
334
|
+
result.ShouldHaveValidationErrorFor(x => x.Items)
|
|
335
|
+
.WithErrorMessage("*at least one item*");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
[Fact]
|
|
339
|
+
public void CreateValidator_WhenItemsExceedMaxCount_ShouldHaveError()
|
|
340
|
+
{
|
|
341
|
+
// Arrange
|
|
342
|
+
var request = new Create{{name}}Request
|
|
343
|
+
{
|
|
344
|
+
Code = "TEST",
|
|
345
|
+
Items = Enumerable.Range(1, 101).Select(i => $"Item-{i}").ToList()
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// Act
|
|
349
|
+
var result = _createValidator.TestValidate(request);
|
|
350
|
+
|
|
351
|
+
// Assert
|
|
352
|
+
result.ShouldHaveValidationErrorFor(x => x.Items)
|
|
353
|
+
.WithErrorMessage("*maximum*100*");
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
#endregion
|
|
357
|
+
|
|
358
|
+
#region Cross-Field Validation
|
|
359
|
+
|
|
360
|
+
[Fact]
|
|
361
|
+
public void CreateValidator_WhenDependentFieldsMissing_ShouldHaveError()
|
|
362
|
+
{
|
|
363
|
+
// Arrange
|
|
364
|
+
var request = new Create{{name}}Request
|
|
365
|
+
{
|
|
366
|
+
Code = "TEST",
|
|
367
|
+
RequiresApproval = true,
|
|
368
|
+
ApproverEmail = null // Required when RequiresApproval is true
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// Act
|
|
372
|
+
var result = _createValidator.TestValidate(request);
|
|
373
|
+
|
|
374
|
+
// Assert
|
|
375
|
+
result.ShouldHaveValidationErrorFor(x => x.ApproverEmail)
|
|
376
|
+
.WithErrorMessage("*required when*");
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
#endregion
|
|
380
|
+
|
|
381
|
+
#region Security Validation
|
|
382
|
+
|
|
383
|
+
[Theory]
|
|
384
|
+
[InlineData("<script>alert('xss')</script>")]
|
|
385
|
+
[InlineData("'; DROP TABLE Users; --")]
|
|
386
|
+
[InlineData("{{'{{'}}constructor{{'}}'}}")]
|
|
387
|
+
public void CreateValidator_WhenInputContainsMaliciousContent_ShouldHaveError(string maliciousInput)
|
|
388
|
+
{
|
|
389
|
+
// Arrange
|
|
390
|
+
var request = new Create{{name}}Request
|
|
391
|
+
{
|
|
392
|
+
Code = "TEST",
|
|
393
|
+
Description = maliciousInput
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
// Act
|
|
397
|
+
var result = _createValidator.TestValidate(request);
|
|
398
|
+
|
|
399
|
+
// Assert
|
|
400
|
+
result.ShouldHaveValidationErrorFor(x => x.Description)
|
|
401
|
+
.WithErrorMessage("*invalid characters*");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
#endregion
|
|
405
|
+
|
|
406
|
+
#region Error Message Format
|
|
407
|
+
|
|
408
|
+
[Fact]
|
|
409
|
+
public void CreateValidator_WhenMultipleErrors_ShouldReturnAllErrors()
|
|
410
|
+
{
|
|
411
|
+
// Arrange
|
|
412
|
+
var request = new Create{{name}}Request
|
|
413
|
+
{
|
|
414
|
+
Code = "", // Invalid
|
|
415
|
+
Name = new string('A', 500), // Too long
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// Act
|
|
419
|
+
var result = _createValidator.TestValidate(request);
|
|
420
|
+
|
|
421
|
+
// Assert
|
|
422
|
+
result.Errors.Should().HaveCountGreaterThan(1);
|
|
423
|
+
result.ShouldHaveValidationErrorFor(x => x.Code);
|
|
424
|
+
result.ShouldHaveValidationErrorFor(x => x.Name);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
#endregion
|
|
428
|
+
}
|
|
@@ -27,15 +27,15 @@ Edit `ralph.config.yaml` to customize:
|
|
|
27
27
|
|
|
28
28
|
Ralph **requires** these MCP servers to function:
|
|
29
29
|
|
|
30
|
-
### 1.
|
|
30
|
+
### 1. Install packages
|
|
31
31
|
```bash
|
|
32
|
-
npm install -g @atlashub/smartstack-
|
|
32
|
+
npm install -g @atlashub/smartstack-cli
|
|
33
33
|
npm install -g @upstash/context7-mcp
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
### 2. Register in Claude Code
|
|
37
37
|
```bash
|
|
38
|
-
claude mcp add smartstack -- npx @atlashub/smartstack-mcp
|
|
38
|
+
claude mcp add smartstack -- npx -p @atlashub/smartstack-cli smartstack-mcp
|
|
39
39
|
claude mcp add context7 -- npx @upstash/context7-mcp
|
|
40
40
|
```
|
|
41
41
|
|
|
@@ -13,8 +13,8 @@ mcp:
|
|
|
13
13
|
health_check: true
|
|
14
14
|
|
|
15
15
|
- name: smartstack
|
|
16
|
-
description: "SmartStack validation and scaffolding"
|
|
17
|
-
install_command: "
|
|
16
|
+
description: "SmartStack validation and scaffolding (bundled in CLI)"
|
|
17
|
+
install_command: "claude mcp add smartstack -- npx -p @atlashub/smartstack-cli smartstack-mcp"
|
|
18
18
|
health_check: true
|
|
19
19
|
|
|
20
20
|
# Behavior when MCP is unavailable
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: admin
|
|
3
|
+
description: Admin account management - reset passwords, manage local admin accounts
|
|
4
|
+
argument-hint: "<subcommand> [options]"
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<objective>
|
|
9
|
+
Manage SmartStack admin accounts. Wraps the `smartstack admin` CLI command with guided workflows.
|
|
10
|
+
|
|
11
|
+
**WARNING:** This skill modifies database records directly. Always confirm before executing.
|
|
12
|
+
</objective>
|
|
13
|
+
|
|
14
|
+
<quick_start>
|
|
15
|
+
```bash
|
|
16
|
+
/admin reset # Reset local admin password (guided)
|
|
17
|
+
/admin reset --force # Reset without confirmation
|
|
18
|
+
```
|
|
19
|
+
</quick_start>
|
|
20
|
+
|
|
21
|
+
<workflow>
|
|
22
|
+
|
|
23
|
+
## Subcommands
|
|
24
|
+
|
|
25
|
+
| Command | Description |
|
|
26
|
+
|---------|-------------|
|
|
27
|
+
| `/admin reset` | Reset the localAdmin account password |
|
|
28
|
+
|
|
29
|
+
## /admin reset
|
|
30
|
+
|
|
31
|
+
1. **Detect SmartStack.app** - Look for `.smartstack/config.json` or `SmartStack.Api/` directory
|
|
32
|
+
2. **Run CLI command:**
|
|
33
|
+
```bash
|
|
34
|
+
smartstack admin reset
|
|
35
|
+
```
|
|
36
|
+
3. **If `--force` flag passed:**
|
|
37
|
+
```bash
|
|
38
|
+
smartstack admin reset --force
|
|
39
|
+
```
|
|
40
|
+
4. **Report result** - Show new credentials or error message
|
|
41
|
+
|
|
42
|
+
</workflow>
|