| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 2 | 35 | 31 | 3% |
| Commands | 0 | 1 | 0 | 0% |
| Section tags | 4 | 3 | 0 | 57% |
What each file covers
Sections
2 shared · 35 only in A · 31 only in B- − Testing Standards
- − Framework Stack
- − Primary Testing Tools
- − Test Setup
- − Test File Patterns
- − Naming Conventions
- − Directory Structure
- − Mocha Configuration
- − Standard Setup (.mocharc.json)
- − TypeScript Compilation
- − Test Structure
- − Standard Test Pattern
- − Async/Await Pattern
- − Mocking Patterns
- − Class Mocking
- − Function Stubs
- − Command Testing
- − OCLIF Test Pattern
- − Command Flag Testing
- − Error Testing
- − Error Types
- − Test Data Management
- − Mock Data Organization
- − Test Helpers
- − Coverage
- − Coverage Goals
- − Coverage Reporting
- − Run tests with coverage
- − Coverage reports generated in:
- − - coverage/index.html (HTML report)
- − - coverage/coverage-summary.json (JSON report)
- − Critical Testing Rules
- − Test Organization
- − Async Test Patterns
- − Isolation
- + OCLIF Command Standards
- + Command Structure
- + Standard Command Pattern
- + Base Classes
- + Command Base Class
- + Custom Base Classes
- + OCLIF Configuration
- + Package.json Setup
- + Command Topics
- + Command Naming
- + Flag Management
- + Flag Definition Patterns
- + Flag Parsing
- + Standard Error Pattern
- + User-Friendly Messages
- + Validation Patterns
- + Early Validation
- + Progress and Logging
- + User Feedback
- + Progress Indication
- + Command Delegation
- + Service Layer Separation
- + Testing Commands
- + OCLIF Test Support
- + Log Integration
- + Debug Logging
- + Error Context
- + Multi-Topic Commands
- + Nested Command Structure
- + Command Organization
- + Clear Help Text
- Error Handling
- Best Practices
Commands
0 shared · 1 only in A · 0 only in B- − pnpm test:coverage
Section tags
4 shared · 3 only in A · 0 only in B- − types
- − testing-strategy
- − do-not
- setup
- test
- code-style
- architecture
Line diff
contentstack/cli · .cursor/rules/testing.mdc
@@ −1 @@
1---
2description: 'Testing patterns and TDD workflow'
3globs: ['**/test/**/*.ts', '**/test/**/*.js', '**/__tests__/**/*.ts', '**/*.spec.ts', '**/*.test.ts']
4alwaysApply: true
5---
6
7# Testing Standards
8
9## Framework Stack
10
11### Primary Testing Tools
12- **Mocha** - Test runner (used across all packages)
13- **Chai** - Assertion library
14- **@oclif/test** - Command testing support (for plugin packages)
15
16### Test Setup
17- TypeScript compilation via ts-node/register
18- Source map support for stack traces
19- Global test timeout: 30 seconds (configurable per package)
20
21## Test File Patterns
22
23### Naming Conventions
24- **Primary**: `*.test.ts` (standard pattern across all packages)
25- **Location**: `test/unit/**/*.test.ts` (most packages)
26
27### Directory Structure
28```
29packages/*/
30├── test/
31│ └── unit/
32│ ├── commands/ # Command-specific tests
33│ ├── services/ # Service/business logic tests
34│ └── utils/ # Utility function tests
35└── src/ # Source code
36 ├── commands/ # CLI commands
37 ├── services/ # Business logic
38 └── utils/ # Utilities
39```
40
41## Mocha Configuration
42
43### Standard Setup (.mocharc.json)
44```json
45{
46 "require": [
47 "test/helpers/init.js",
48 "ts-node/register",
49 "source-map-support/register"
50 ],
51 "recursive": true,
52 "timeout": 30000,
53 "spec": "test/**/*.test.ts"
54}
55```
56
57### TypeScript Compilation
58```json
59// package.json scripts
60{
61 "test": "mocha \"test/unit/**/*.test.ts\"",
62 "test:coverage": "nyc mocha \"test/unit/**/*.test.ts\""
63}
64```
65
66## Test Structure
67
68### Standard Test Pattern
69```typescript
70// ✅ GOOD - Comprehensive test structure
71describe('ConfigService', () => {
72 let service: ConfigService;
73
74 beforeEach(() => {
75 service = new ConfigService();
76 });
77
78 describe('loadConfig()', () => {
79 it('should load configuration successfully', async () => {
80 // Arrange
81 const expectedConfig = { region: 'us' };
82
83 // Act
84 const result = await service.loadConfig();
85
86 // Assert
87 expect(result).to.deep.equal(expectedConfig);
88 });
89
90 it('should handle missing configuration', async () => {
91 // Arrange & Act & Assert
92 await expect(service.loadConfig()).to.be.rejectedWith('Config not found');
93 });
94 });
95});
96```
97
98### Async/Await Pattern
99```typescript
100// ✅ GOOD - Use async/await in tests
101it('should process data asynchronously', async () => {
102 const result = await service.processAsync();
103 expect(result).to.exist;
104});
105
106// ✅ GOOD - Explicit Promise handling
107it('should return a promise', () => {
108 return service.asyncMethod().then(result => {
109 expect(result).to.be.true;
110 });
111});
112```
113
114## Mocking Patterns
115
116### Class Mocking
117```typescript
118// ✅ GOOD - Mock class dependencies
119class MockConfigService {
120 async loadConfig() {
121 return { region: 'us' };
122 }
123}
124
125it('should use mocked service', async () => {
126 const mockService = new MockConfigService();
127 const result = await mockService.loadConfig();
128 expect(result.region).to.equal('us');
129});
130```
131
132### Function Stubs
133```typescript
134// ✅ GOOD - Stub module functions if needed
135beforeEach(() => {
136 // Stub file system operations
137 // Stub network calls
138});
139
140afterEach(() => {
141 // Restore original implementations
142});
143```
144
145## Command Testing
146
147### OCLIF Test Pattern
148```typescript
149// ✅ GOOD - Test commands with @oclif/test
150import { test } from '@oclif/test';
151
152describe('cm:config:region', () => {
153 test
154 .stdout()
155 .command(['cm:config:region', '--help'])
156 .it('shows help message', ctx => {
157 expect(ctx.stdout).to.contain('Display region');
158 });
159
160 test
161 .stdout()
162 .command(['cm:config:region'])
163 .it('shows current region', ctx => {
164 expect(ctx.stdout).to.contain('us');
165 });
166});
167```
168
169### Command Flag Testing
170```typescript
171// ✅ GOOD - Test command flags and arguments
172describe('cm:config:set', () => {
173 test
174 .command(['cm:config:set', '--help'])
175 .it('shows usage information');
176
177 test
178 .command(['cm:config:set', '--region', 'eu'])
179 .it('sets region to eu');
180});
181```
182
183## Error Testing
184
185### Error Handling
186```typescript
187// ✅ GOOD - Test error scenarios
188it('should throw ValidationError on invalid input', async () => {
189 const invalidInput = '';
190 await expect(service.validate(invalidInput))
191 .to.be.rejectedWith('Invalid input');
192});
193
194it('should handle network errors gracefully', async () => {
195 // Mock network failure
196 const result = await service.fetchWithRetry();
197 expect(result).to.be.null;
198});
199```
200
201### Error Types
202```typescript
203// ✅ GOOD - Test specific error types
204it('should throw appropriate error', async () => {
205 try {
206 await service.failingOperation();
207 } catch (error) {
208 expect(error).to.be.instanceof(ValidationError);
209 expect(error.code).to.equal('INVALID_CONFIG');
210 }
211});
212```
213
214## Test Data Management
215
216### Mock Data Organization
217```typescript
218// ✅ GOOD - Organize test data
219const mockData = {
220 validConfig: {
221 region: 'us',
222 timeout: 30000,
223 },
224 invalidConfig: {
225 region: '',
226 },
227 users: [
228 { email: 'user1@example.com', name: 'User 1' },
229 { email: 'user2@example.com', name: 'User 2' },
230 ],
231};
232```
233
234### Test Helpers
235```typescript
236// ✅ GOOD - Create reusable test utilities
237export function createMockConfig(overrides?: Partial<Config>): Config {
238 return {
239 region: 'us',
240 timeout: 30000,
241 ...overrides,
242 };
243}
244
245export function createMockService(
246 config: Config = createMockConfig()
247): ConfigService {
248 return new ConfigService(config);
249}
250```
251
252## Coverage
253
254### Coverage Goals
255- **Team aspiration**: 80% minimum coverage
256- **Current enforcement**: Applied consistently across packages
257- **Focus areas**: Critical business logic and error paths
258
259### Coverage Reporting
260```bash
261# Run tests with coverage
262pnpm test:coverage
263
264# Coverage reports generated in:
265# - coverage/index.html (HTML report)
266# - coverage/coverage-summary.json (JSON report)
267```
268
269## Critical Testing Rules
270
271- **No real external calls** - Mock all dependencies
272- **Test both success and failure paths** - Cover error scenarios completely
273- **One assertion per test** - Focus each test on single behavior
274- **Use descriptive test names** - Test name should explain what's tested
275- **Arrange-Act-Assert** - Follow AAA pattern consistently
276- **Test command validation** - Verify flag validation and error messages
277- **Clean up after tests** - Restore any mocked state
278
279## Best Practices
280
281### Test Organization
282```typescript
283// ✅ GOOD - Organize related tests
284describe('AuthCommand', () => {
285 describe('login', () => {
286 it('should authenticate user');
287 it('should save token');
288 });
289
290 describe('logout', () => {
291 it('should clear token');
292 it('should reset config');
293 });
294});
295```
296
297### Async Test Patterns
298```typescript
299// ✅ GOOD - Handle async operations properly
300it('should complete async operation', async () => {
301 const promise = service.asyncMethod();
302 expect(promise).to.be.instanceof(Promise);
303
304 const result = await promise;
305 expect(result).to.equal('success');
306});
307```
308
309### Isolation
310```typescript
311// ✅ GOOD - Ensure test isolation
312describe('ConfigService', () => {
313 let service: ConfigService;
314
315 beforeEach(() => {
316 service = new ConfigService();
317 });
318
319 afterEach(() => {
320 // Clean up resources
321 });
322});
323```
324
contentstack/cli · .cursor/rules/oclif-commands.mdc
@@ +1 @@
1---
2description: 'OCLIF command development patterns and CLI best practices'
3globs: ['**/commands/**/*.ts', '**/base-command.ts']
4alwaysApply: false
5---
6
7# OCLIF Command Standards
8
9## Command Structure
10
11### Standard Command Pattern
12```typescript
13// ✅ GOOD - Standard command structure
14import { Command } from '@contentstack/cli-command';
15import { cliux, flags, FlagInput, handleAndLogError } from '@contentstack/cli-utilities';
16
17export default class ConfigSetCommand extends Command {
18 static description = 'Set CLI configuration values';
19
20 static flags: FlagInput = {
21 region: flags.string({
22 char: 'r',
23 description: 'Set region (us/eu)',
24 }),
25 alias: flags.string({
26 char: 'a',
27 description: 'Configuration alias',
28 }),
29 };
30
31 static examples = [
32 'csdx config:set --region eu',
33 'csdx config:set --region us --alias default',
34 ];
35
36 async run(): Promise<void> {
37 try {
38 const { flags: configFlags } = await this.parse(ConfigSetCommand);
39 // Command logic here
40 } catch (error) {
41 handleAndLogError(error, { module: 'config-set' });
42 }
43 }
44}
45```
46
47## Base Classes
48
49### Command Base Class
50```typescript
51// ✅ GOOD - Extend Command from @contentstack/cli-command
52import { Command } from '@contentstack/cli-command';
53
54export default class MyCommand extends Command {
55 async run(): Promise<void> {
56 // Command implementation
57 }
58}
59```
60
61### Custom Base Classes
62```typescript
63// ✅ GOOD - Create custom base classes for shared functionality
64export abstract class BaseCommand<T extends typeof Command> extends Command {
65 protected contextDetails = {
66 command: this.id || 'unknown',
67 };
68
69 async init(): Promise<void> {
70 await super.init();
71 log.debug('Command initialized', this.contextDetails);
72 }
73}
74```
75
76## OCLIF Configuration
77
78### Package.json Setup
79```json
80{
81 "oclif": {
82 "commands": "./lib/commands",
83 "bin": "csdx",
84 "topicSeparator": ":"
85 }
86}
87```
88
89### Command Topics
90- All commands use `cm` topic: `cm:config:set`, `cm:auth:login`
91- Built commands live in `lib/commands` (compiled from `src/commands`)
92- Commands use nested directories: `src/commands/config/set.ts` → `cm:config:set`
93
94### Command Naming
95- **Topic hierarchy**: `config/remove/proxy.ts` → `cm:config:remove:proxy`
96- **Descriptive names**: Use verb-noun pattern (`set`, `remove`, `show`)
97- **Grouping**: Related commands share parent topics
98
99## Flag Management
100
101### Flag Definition Patterns
102```typescript
103// ✅ GOOD - Define flags clearly
104static flags: FlagInput = {
105 'stack-api-key': flags.string({
106 char: 'k',
107 description: 'Stack API key',
108 required: false,
109 }),
110 region: flags.string({
111 char: 'r',
112 description: 'Set region',
113 options: ['us', 'eu'],
114 }),
115 verbose: flags.boolean({
116 char: 'v',
117 description: 'Show verbose output',
118 default: false,
119 }),
120};
121```
122
123### Flag Parsing
124```typescript
125// ✅ GOOD - Parse and validate flags
126async run(): Promise<void> {
127 const { flags: parsedFlags } = await this.parse(MyCommand);
128
129 // Validate flag combinations
130 if (!parsedFlags['stack-api-key'] && !parsedFlags.alias) {
131 this.error('Either --stack-api-key or --alias is required');
132 }
133
134 // Use parsed flags
135 const region = parsedFlags.region || 'us';
136}
137```
138
139## Error Handling
140
141### Standard Error Pattern
142```typescript
143// ✅ GOOD - Use handleAndLogError from utilities
144try {
145 await this.executeCommand();
146} catch (error) {
147 handleAndLogError(error, { module: 'my-command' });
148}
149```
150
151### User-Friendly Messages
152```typescript
153// ✅ GOOD - Clear user feedback
154import { cliux } from '@contentstack/cli-utilities';
155
156// Success message
157cliux.success('Configuration updated successfully', { color: 'green' });
158
159// Error message
160cliux.error('Invalid region specified', { color: 'red' });
161
162// Info message
163cliux.print('Setting region to eu', { color: 'blue' });
164```
165
166## Validation Patterns
167
168### Early Validation
169```typescript
170// ✅ GOOD - Validate flags early
171async run(): Promise<void> {
172 const { flags } = await this.parse(MyCommand);
173
174 // Validate required flags
175 if (!flags.region) {
176 this.error('--region is required');
177 }
178
179 // Validate flag values
180 if (!['us', 'eu'].includes(flags.region)) {
181 this.error('Region must be "us" or "eu"');
182 }
183
184 // Proceed with validated input
185}
186```
187
188## Progress and Logging
189
190### User Feedback
191```typescript
192// ✅ GOOD - Provide user feedback
193import { log, cliux } from '@contentstack/cli-utilities';
194
195// Regular logging
196this.log('Starting configuration update...');
197
198// Debug logging
199log.debug('Detailed operation information', { context: 'data' });
200
201// Status messages
202cliux.print('Processing...', { color: 'blue' });
203```
204
205### Progress Indication
206```typescript
207// ✅ GOOD - Show progress for long operations
208cliux.print('Processing items...', { color: 'blue' });
209let count = 0;
210for (const item of items) {
211 await this.processItem(item);
212 count++;
213 cliux.print(`Processed ${count}/${items.length} items`, { color: 'blue' });
214}
215```
216
217## Command Delegation
218
219### Service Layer Separation
220```typescript
221// ✅ GOOD - Commands orchestrate, services implement
222async run(): Promise<void> {
223 try {
224 const { flags } = await this.parse(MyCommand);
225 const config = this.buildConfig(flags);
226 const service = new ConfigService(config);
227
228 await service.execute();
229 cliux.success('Operation completed successfully');
230 } catch (error) {
231 this.handleError(error);
232 }
233}
234```
235
236## Testing Commands
237
238### OCLIF Test Support
239```typescript
240// ✅ GOOD - Use @oclif/test for command testing
241import { test } from '@oclif/test';
242
243describe('cm:config:set', () => {
244 test
245 .stdout()
246 .command(['cm:config:set', '--help'])
247 .it('shows help', ctx => {
248 expect(ctx.stdout).to.contain('Set CLI configuration');
249 });
250
251 test
252 .stdout()
253 .command(['cm:config:set', '--region', 'eu'])
254 .it('sets region to eu', ctx => {
255 expect(ctx.stdout).to.contain('success');
256 });
257});
258```
259
260## Log Integration
261
262### Debug Logging
263```typescript
264// ✅ GOOD - Use structured debug logging
265import { log } from '@contentstack/cli-utilities';
266
267log.debug('Command started', {
268 command: this.id,
269 flags: this.flags,
270 timestamp: new Date().toISOString(),
271});
272
273log.debug('Processing complete', {
274 itemsProcessed: count,
275 module: 'my-command',
276});
277```
278
279### Error Context
280```typescript
281// ✅ GOOD - Include context in error handling
282try {
283 await operation();
284} catch (error) {
285 handleAndLogError(error, {
286 module: 'config-set',
287 command: 'cm:config:set',
288 flags: { region: 'eu' },
289 });
290}
291```
292
293## Multi-Topic Commands
294
295### Nested Command Structure
296```typescript
297// File: src/commands/config/show.ts
298export default class ShowConfigCommand extends Command {
299 static description = 'Show current configuration';
300 static examples = ['csdx config:show'];
301 async run(): Promise<void> { }
302}
303
304// File: src/commands/config/set.ts
305export default class SetConfigCommand extends Command {
306 static description = 'Set configuration values';
307 static examples = ['csdx config:set --region eu'];
308 async run(): Promise<void> { }
309}
310
311// Generated commands:
312// - cm:config:show
313// - cm:config:set
314```
315
316## Best Practices
317
318### Command Organization
319```typescript
320// ✅ GOOD - Well-organized command
321export default class MyCommand extends Command {
322 static description = 'Clear, concise description';
323
324 static flags: FlagInput = {
325 // Define all flags
326 };
327
328 static examples = [
329 'csdx my:command',
330 'csdx my:command --flag value',
331 ];
332
333 async run(): Promise<void> {
334 try {
335 const { flags } = await this.parse(MyCommand);
336 await this.execute(flags);
337 } catch (error) {
338 handleAndLogError(error, { module: 'my-command' });
339 }
340 }
341
342 private async execute(flags: Flags<typeof MyCommand>): Promise<void> {
343 // Implementation
344 }
345}
346```
347
348### Clear Help Text
349- Write description as action-oriented statement
350- Provide multiple examples for common use cases
351- Document each flag with clear description
352- Show output format or examples of results
353
@@ −1 +1 @@
11 ---
2−description: 'Testing patterns and TDD workflow'
3−globs: ['**/test/**/*.ts', '**/test/**/*.js', '**/__tests__/**/*.ts', '**/*.spec.ts', '**/*.test.ts']
4−alwaysApply: true
2+description: 'OCLIF command development patterns and CLI best practices'
3+globs: ['**/commands/**/*.ts', '**/base-command.ts']
4+alwaysApply: false
55 ---
66
7−# Testing Standards
7+# OCLIF Command Standards
88
9−## Framework Stack
9+## Command Structure
1010
11−### Primary Testing Tools
12−- **Mocha** - Test runner (used across all packages)
13−- **Chai** - Assertion library
14−- **@oclif/test** - Command testing support (for plugin packages)
11+### Standard Command Pattern
12+```typescript
13+// ✅ GOOD - Standard command structure
14+import { Command } from '@contentstack/cli-command';
15+import { cliux, flags, FlagInput, handleAndLogError } from '@contentstack/cli-utilities';
1516
16−### Test Setup
17−- TypeScript compilation via ts-node/register
18−- Source map support for stack traces
19−- Global test timeout: 30 seconds (configurable per package)
17+export default class ConfigSetCommand extends Command {
18+ static description = 'Set CLI configuration values';
19+
20+ static flags: FlagInput = {
21+ region: flags.string({
22+ char: 'r',
23+ description: 'Set region (us/eu)',
24+ }),
25+ alias: flags.string({
26+ char: 'a',
27+ description: 'Configuration alias',
28+ }),
29+ };
30+
31+ static examples = [
32+ 'csdx config:set --region eu',
33+ 'csdx config:set --region us --alias default',
34+ ];
35+
36+ async run(): Promise<void> {
37+ try {
38+ const { flags: configFlags } = await this.parse(ConfigSetCommand);
39+ // Command logic here
40+ } catch (error) {
41+ handleAndLogError(error, { module: 'config-set' });
42+ }
43+ }
44+}
45+```
2046
21−## Test File Patterns
47+## Base Classes
2248
23−### Naming Conventions
24−- **Primary**: `*.test.ts` (standard pattern across all packages)
25−- **Location**: `test/unit/**/*.test.ts` (most packages)
49+### Command Base Class
50+```typescript
51+// ✅ GOOD - Extend Command from @contentstack/cli-command
52+import { Command } from '@contentstack/cli-command';
2653
27−### Directory Structure
54+export default class MyCommand extends Command {
55+ async run(): Promise<void> {
56+ // Command implementation
57+ }
58+}
2859 ```
29−packages/*/
30−├── test/
31−│ └── unit/
32−│ ├── commands/ # Command-specific tests
33−│ ├── services/ # Service/business logic tests
34−│ └── utils/ # Utility function tests
35−└── src/ # Source code
36− ├── commands/ # CLI commands
37− ├── services/ # Business logic
38− └── utils/ # Utilities
39−```
4060
41−## Mocha Configuration
61+### Custom Base Classes
62+```typescript
63+// ✅ GOOD - Create custom base classes for shared functionality
64+export abstract class BaseCommand<T extends typeof Command> extends Command {
65+ protected contextDetails = {
66+ command: this.id || 'unknown',
67+ };
4268
43−### Standard Setup (.mocharc.json)
44−```json
45−{
46− "require": [
47− "test/helpers/init.js",
48− "ts-node/register",
49− "source-map-support/register"
50− ],
51− "recursive": true,
52− "timeout": 30000,
53− "spec": "test/**/*.test.ts"
69+ async init(): Promise<void> {
70+ await super.init();
71+ log.debug('Command initialized', this.contextDetails);
72+ }
5473 }
5574 ```
5675
57−### TypeScript Compilation
76+## OCLIF Configuration
77+
78+### Package.json Setup
5879 ```json
59−// package.json scripts
6080 {
61− "test": "mocha \"test/unit/**/*.test.ts\"",
62− "test:coverage": "nyc mocha \"test/unit/**/*.test.ts\""
81+ "oclif": {
82+ "commands": "./lib/commands",
83+ "bin": "csdx",
84+ "topicSeparator": ":"
85+ }
6386 }
6487 ```
6588
66−## Test Structure
89+### Command Topics
90+- All commands use `cm` topic: `cm:config:set`, `cm:auth:login`
91+- Built commands live in `lib/commands` (compiled from `src/commands`)
92+- Commands use nested directories: `src/commands/config/set.ts` → `cm:config:set`
6793
68−### Standard Test Pattern
94+### Command Naming
95+- **Topic hierarchy**: `config/remove/proxy.ts` → `cm:config:remove:proxy`
96+- **Descriptive names**: Use verb-noun pattern (`set`, `remove`, `show`)
97+- **Grouping**: Related commands share parent topics
98+
99+## Flag Management
100+
101+### Flag Definition Patterns
69102 ```typescript
70−// ✅ GOOD - Comprehensive test structure
71−describe('ConfigService', () => {
72− let service: ConfigService;
103+// ✅ GOOD - Define flags clearly
104+static flags: FlagInput = {
105+ 'stack-api-key': flags.string({
106+ char: 'k',
107+ description: 'Stack API key',
108+ required: false,
109+ }),
110+ region: flags.string({
111+ char: 'r',
112+ description: 'Set region',
113+ options: ['us', 'eu'],
114+ }),
115+ verbose: flags.boolean({
116+ char: 'v',
117+ description: 'Show verbose output',
118+ default: false,
119+ }),
120+};
121+```
73122
74− beforeEach(() => {
75− service = new ConfigService();
76− });
123+### Flag Parsing
124+```typescript
125+// ✅ GOOD - Parse and validate flags
126+async run(): Promise<void> {
127+ const { flags: parsedFlags } = await this.parse(MyCommand);
128+
129+ // Validate flag combinations
130+ if (!parsedFlags['stack-api-key'] && !parsedFlags.alias) {
131+ this.error('Either --stack-api-key or --alias is required');
132+ }
133+
134+ // Use parsed flags
135+ const region = parsedFlags.region || 'us';
136+}
137+```
77138
78− describe('loadConfig()', () => {
79− it('should load configuration successfully', async () => {
80− // Arrange
81− const expectedConfig = { region: 'us' };
82−
83− // Act
84− const result = await service.loadConfig();
85−
86− // Assert
87− expect(result).to.deep.equal(expectedConfig);
88− });
139+## Error Handling
89140
90− it('should handle missing configuration', async () => {
91− // Arrange & Act & Assert
92− await expect(service.loadConfig()).to.be.rejectedWith('Config not found');
93− });
94− });
95−});
141+### Standard Error Pattern
142+```typescript
143+// ✅ GOOD - Use handleAndLogError from utilities
144+try {
145+ await this.executeCommand();
146+} catch (error) {
147+ handleAndLogError(error, { module: 'my-command' });
148+}
96149 ```
97150
98−### Async/Await Pattern
151+### User-Friendly Messages
99152 ```typescript
100−// ✅ GOOD - Use async/await in tests
101−it('should process data asynchronously', async () => {
102− const result = await service.processAsync();
103− expect(result).to.exist;
104−});
153+// ✅ GOOD - Clear user feedback
154+import { cliux } from '@contentstack/cli-utilities';
105155
106−// ✅ GOOD - Explicit Promise handling
107−it('should return a promise', () => {
108− return service.asyncMethod().then(result => {
109− expect(result).to.be.true;
110− });
111−});
156+// Success message
157+cliux.success('Configuration updated successfully', { color: 'green' });
158+
159+// Error message
160+cliux.error('Invalid region specified', { color: 'red' });
161+
162+// Info message
163+cliux.print('Setting region to eu', { color: 'blue' });
112164 ```
113165
114−## Mocking Patterns
166+## Validation Patterns
115167
116−### Class Mocking
168+### Early Validation
117169 ```typescript
118−// ✅ GOOD - Mock class dependencies
119−class MockConfigService {
120− async loadConfig() {
121− return { region: 'us' };
170+// ✅ GOOD - Validate flags early
171+async run(): Promise<void> {
172+ const { flags } = await this.parse(MyCommand);
173+
174+ // Validate required flags
175+ if (!flags.region) {
176+ this.error('--region is required');
122177 }
178+
179+ // Validate flag values
180+ if (!['us', 'eu'].includes(flags.region)) {
181+ this.error('Region must be "us" or "eu"');
182+ }
183+
184+ // Proceed with validated input
123185 }
186+```
124187
125−it('should use mocked service', async () => {
126− const mockService = new MockConfigService();
127− const result = await mockService.loadConfig();
128− expect(result.region).to.equal('us');
129−});
188+## Progress and Logging
189+
190+### User Feedback
191+```typescript
192+// ✅ GOOD - Provide user feedback
193+import { log, cliux } from '@contentstack/cli-utilities';
194+
195+// Regular logging
196+this.log('Starting configuration update...');
197+
198+// Debug logging
199+log.debug('Detailed operation information', { context: 'data' });
200+
201+// Status messages
202+cliux.print('Processing...', { color: 'blue' });
130203 ```
131204
132−### Function Stubs
205+### Progress Indication
133206 ```typescript
134−// ✅ GOOD - Stub module functions if needed
135−beforeEach(() => {
136− // Stub file system operations
137− // Stub network calls
138−});
207+// ✅ GOOD - Show progress for long operations
208+cliux.print('Processing items...', { color: 'blue' });
209+let count = 0;
210+for (const item of items) {
211+ await this.processItem(item);
212+ count++;
213+ cliux.print(`Processed ${count}/${items.length} items`, { color: 'blue' });
214+}
215+```
139216
140−afterEach(() => {
141− // Restore original implementations
142−});
217+## Command Delegation
218+
219+### Service Layer Separation
220+```typescript
221+// ✅ GOOD - Commands orchestrate, services implement
222+async run(): Promise<void> {
223+ try {
224+ const { flags } = await this.parse(MyCommand);
225+ const config = this.buildConfig(flags);
226+ const service = new ConfigService(config);
227+
228+ await service.execute();
229+ cliux.success('Operation completed successfully');
230+ } catch (error) {
231+ this.handleError(error);
232+ }
233+}
143234 ```
144235
145−## Command Testing
236+## Testing Commands
146237
147−### OCLIF Test Pattern
238+### OCLIF Test Support
148239 ```typescript
149−// ✅ GOOD - Test commands with @oclif/test
240+// ✅ GOOD - Use @oclif/test for command testing
150241 import { test } from '@oclif/test';
151242
152−describe('cm:config:region', () => {
243+describe('cm:config:set', () => {
153244 test
154245 .stdout()
155− .command(['cm:config:region', '--help'])
156− .it('shows help message', ctx => {
157− expect(ctx.stdout).to.contain('Display region');
246+ .command(['cm:config:set', '--help'])
247+ .it('shows help', ctx => {
248+ expect(ctx.stdout).to.contain('Set CLI configuration');
158249 });
159250
160251 test
161252 .stdout()
162− .command(['cm:config:region'])
163− .it('shows current region', ctx => {
164− expect(ctx.stdout).to.contain('us');
253+ .command(['cm:config:set', '--region', 'eu'])
254+ .it('sets region to eu', ctx => {
255+ expect(ctx.stdout).to.contain('success');
165256 });
166257 });
167258 ```
168259
169−### Command Flag Testing
260+## Log Integration
261+
262+### Debug Logging
170263 ```typescript
171−// ✅ GOOD - Test command flags and arguments
172−describe('cm:config:set', () => {
173− test
174− .command(['cm:config:set', '--help'])
175− .it('shows usage information');
264+// ✅ GOOD - Use structured debug logging
265+import { log } from '@contentstack/cli-utilities';
176266
177− test
178− .command(['cm:config:set', '--region', 'eu'])
179− .it('sets region to eu');
267+log.debug('Command started', {
268+ command: this.id,
269+ flags: this.flags,
270+ timestamp: new Date().toISOString(),
180271 });
181−```
182272
183−## Error Testing
184−
185−### Error Handling
186−```typescript
187−// ✅ GOOD - Test error scenarios
188−it('should throw ValidationError on invalid input', async () => {
189− const invalidInput = '';
190− await expect(service.validate(invalidInput))
191− .to.be.rejectedWith('Invalid input');
273+log.debug('Processing complete', {
274+ itemsProcessed: count,
275+ module: 'my-command',
192276 });
193−
194−it('should handle network errors gracefully', async () => {
195− // Mock network failure
196− const result = await service.fetchWithRetry();
197− expect(result).to.be.null;
198−});
199277 ```
200278
201−### Error Types
279+### Error Context
202280 ```typescript
203−// ✅ GOOD - Test specific error types
204−it('should throw appropriate error', async () => {
205− try {
206− await service.failingOperation();
207− } catch (error) {
208− expect(error).to.be.instanceof(ValidationError);
209− expect(error.code).to.equal('INVALID_CONFIG');
210− }
211−});
281+// ✅ GOOD - Include context in error handling
282+try {
283+ await operation();
284+} catch (error) {
285+ handleAndLogError(error, {
286+ module: 'config-set',
287+ command: 'cm:config:set',
288+ flags: { region: 'eu' },
289+ });
290+}
212291 ```
213292
214−## Test Data Management
293+## Multi-Topic Commands
215294
216−### Mock Data Organization
295+### Nested Command Structure
217296 ```typescript
218−// ✅ GOOD - Organize test data
219−const mockData = {
220− validConfig: {
221− region: 'us',
222− timeout: 30000,
223− },
224− invalidConfig: {
225− region: '',
226− },
227− users: [
228− { email: 'user1@example.com', name: 'User 1' },
229− { email: 'user2@example.com', name: 'User 2' },
230− ],
231−};
232−```
233−
234−### Test Helpers
235−```typescript
236−// ✅ GOOD - Create reusable test utilities
237−export function createMockConfig(overrides?: Partial<Config>): Config {
238− return {
239− region: 'us',
240− timeout: 30000,
241− ...overrides,
242− };
297+// File: src/commands/config/show.ts
298+export default class ShowConfigCommand extends Command {
299+ static description = 'Show current configuration';
300+ static examples = ['csdx config:show'];
301+ async run(): Promise<void> { }
243302 }
244303
245−export function createMockService(
246− config: Config = createMockConfig()
247−): ConfigService {
248− return new ConfigService(config);
304+// File: src/commands/config/set.ts
305+export default class SetConfigCommand extends Command {
306+ static description = 'Set configuration values';
307+ static examples = ['csdx config:set --region eu'];
308+ async run(): Promise<void> { }
249309 }
250−```
251310
252−## Coverage
253−
254−### Coverage Goals
255−- **Team aspiration**: 80% minimum coverage
256−- **Current enforcement**: Applied consistently across packages
257−- **Focus areas**: Critical business logic and error paths
258−
259−### Coverage Reporting
260−```bash
261−# Run tests with coverage
262−pnpm test:coverage
263−
264−# Coverage reports generated in:
265−# - coverage/index.html (HTML report)
266−# - coverage/coverage-summary.json (JSON report)
311+// Generated commands:
312+// - cm:config:show
313+// - cm:config:set
267314 ```
268315
269−## Critical Testing Rules
270−
271−- **No real external calls** - Mock all dependencies
272−- **Test both success and failure paths** - Cover error scenarios completely
273−- **One assertion per test** - Focus each test on single behavior
274−- **Use descriptive test names** - Test name should explain what's tested
275−- **Arrange-Act-Assert** - Follow AAA pattern consistently
276−- **Test command validation** - Verify flag validation and error messages
277−- **Clean up after tests** - Restore any mocked state
278−
279316 ## Best Practices
280317
281−### Test Organization
318+### Command Organization
282319 ```typescript
283−// ✅ GOOD - Organize related tests
284−describe('AuthCommand', () => {
285− describe('login', () => {
286− it('should authenticate user');
287− it('should save token');
288− });
320+// ✅ GOOD - Well-organized command
321+export default class MyCommand extends Command {
322+ static description = 'Clear, concise description';
289323
290− describe('logout', () => {
291− it('should clear token');
292− it('should reset config');
293− });
294−});
295−```
296−
297−### Async Test Patterns
298−```typescript
299−// ✅ GOOD - Handle async operations properly
300−it('should complete async operation', async () => {
301− const promise = service.asyncMethod();
302− expect(promise).to.be.instanceof(Promise);
324+ static flags: FlagInput = {
325+ // Define all flags
326+ };
303327
304− const result = await promise;
305− expect(result).to.equal('success');
306−});
307−```
308−
309−### Isolation
310−```typescript
311−// ✅ GOOD - Ensure test isolation
312−describe('ConfigService', () => {
313− let service: ConfigService;
328+ static examples = [
329+ 'csdx my:command',
330+ 'csdx my:command --flag value',
331+ ];
314332
315− beforeEach(() => {
316− service = new ConfigService();
317− });
333+ async run(): Promise<void> {
334+ try {
335+ const { flags } = await this.parse(MyCommand);
336+ await this.execute(flags);
337+ } catch (error) {
338+ handleAndLogError(error, { module: 'my-command' });
339+ }
340+ }
318341
319− afterEach(() => {
320− // Clean up resources
321− });
322−});
342+ private async execute(flags: Flags<typeof MyCommand>): Promise<void> {
343+ // Implementation
344+ }
345+}
323346 ```
347+
348+### Clear Help Text
349+- Write description as action-oriented statement
350+- Provide multiple examples for common use cases
351+- Document each flag with clear description
352+- Show output format or examples of results
324353
