bc-code-intelligence-mcp 1.5.6 → 1.5.7

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.
Files changed (23) hide show
  1. package/README.md +153 -407
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +6 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/sdk/bc-code-intel-client.d.ts.map +1 -1
  6. package/dist/sdk/bc-code-intel-client.js +1 -1
  7. package/dist/sdk/bc-code-intel-client.js.map +1 -1
  8. package/dist/types/bc-knowledge.d.ts +4 -4
  9. package/embedded-knowledge/domains/alex-architect/samples/testability-design-patterns.md +223 -0
  10. package/embedded-knowledge/domains/alex-architect/testability-design-patterns.md +77 -0
  11. package/embedded-knowledge/domains/casey-copilot/long-running-session-instructions.md +263 -0
  12. package/embedded-knowledge/domains/casey-copilot/samples/long-running-session-instructions.md +323 -0
  13. package/embedded-knowledge/domains/eva-errors/codeunit-run-pattern.md +159 -0
  14. package/embedded-knowledge/domains/eva-errors/samples/codeunit-run-pattern.md +239 -0
  15. package/embedded-knowledge/domains/eva-errors/samples/try-function-usage.md +195 -0
  16. package/embedded-knowledge/domains/eva-errors/try-function-usage.md +129 -0
  17. package/embedded-knowledge/domains/morgan-market/partner-readiness-analysis.md +201 -0
  18. package/embedded-knowledge/domains/morgan-market/samples/partner-readiness-checklist.md +288 -0
  19. package/embedded-knowledge/domains/quinn-tester/isolation-testing-patterns.md +82 -0
  20. package/embedded-knowledge/domains/quinn-tester/samples/isolation-testing-patterns.md +424 -0
  21. package/embedded-knowledge/domains/roger-reviewer/samples/testability-code-smells.md +256 -0
  22. package/embedded-knowledge/domains/roger-reviewer/testability-code-smells.md +67 -0
  23. package/package.json +2 -2
@@ -0,0 +1,129 @@
1
+ ---
2
+ title: "Try Function Usage Guidelines"
3
+ domain: "eva-errors"
4
+ difficulty: "intermediate"
5
+ bc_versions: "14+"
6
+ tags: ["try-function", "error-handling", "transactions", "external-systems", "best-practices"]
7
+ samples: "samples/try-function-usage.md"
8
+ related_topics:
9
+ - "codeunit-run-pattern.md"
10
+ - "testfield-error-handling.md"
11
+ ---
12
+ # Try Function Usage Guidelines
13
+
14
+ ## Overview
15
+
16
+ Try functions in Business Central provide a mechanism to catch runtime errors without terminating execution. While powerful, they have significant constraints around transaction handling that must be understood to use them correctly.
17
+
18
+ **Core Principle**: Try functions are designed for error detection and graceful handling—not for suppressing errors during write operations.
19
+
20
+ ## When to Use Try Functions
21
+
22
+ ### External System Calls
23
+ Try functions are ideal for wrapping calls to external systems where failures are expected and recoverable:
24
+ - Web service calls that may timeout or fail
25
+ - External API integrations
26
+ - File system operations
27
+ - HTTP client requests
28
+
29
+ External systems are inherently unreliable, and wrapping these calls allows your code to detect failures and respond appropriately rather than crashing.
30
+
31
+ ### Validation Without Termination
32
+ Use try functions when you need to validate data or test conditions without stopping execution:
33
+ - Pre-flight checks before processing
34
+ - Batch validation scenarios
35
+ - User input validation with graceful feedback
36
+
37
+ ### Read-Only Operations
38
+ Try functions work well for read operations that might fail:
39
+ - Record lookups that may not find results
40
+ - Configuration checks
41
+ - Data existence verification
42
+
43
+ ## When NOT to Use Try Functions
44
+
45
+ ### Write Operations in the Call Stack
46
+ **Critical Rule**: Never perform database write operations (INSERT, MODIFY, DELETE) inside a try function or any procedure called from within a try function.
47
+
48
+ This is prohibited because:
49
+ - The transaction behavior becomes unpredictable
50
+ - Partial writes cannot be properly rolled back
51
+ - Data integrity cannot be guaranteed
52
+ - The AL runtime explicitly prevents this pattern
53
+
54
+ ### Suppressing Legitimate Errors
55
+ Don't use try functions to hide errors that indicate real problems:
56
+ - Business rule violations should be reported, not hidden
57
+ - Data validation failures need user attention
58
+ - Configuration errors require administrator action
59
+
60
+ ### Complex Transaction Scenarios
61
+ Avoid try functions in the middle of multi-step transaction processes where you need consistent state management.
62
+
63
+ ## Transaction Behavior
64
+
65
+ ### Try Function Transaction Rules
66
+ When a try function catches an error:
67
+ - Any pending database changes within the try scope are rolled back
68
+ - The calling code continues execution
69
+ - GetLastErrorText() captures the error message
70
+
71
+ ### Write Operation Restrictions
72
+ The Business Central runtime enforces that write transactions cannot occur within a try function's call stack. Attempting this will result in a runtime error indicating the operation is not allowed.
73
+
74
+ ## Implementation Patterns
75
+
76
+ ### External Service Wrapper
77
+ Create try functions specifically for external calls, keeping all database operations outside:
78
+
79
+ ```
80
+ // Correct pattern:
81
+ 1. Prepare data (outside try)
82
+ 2. Call try function for external operation
83
+ 3. Check result
84
+ 4. Write to database based on result (outside try)
85
+ ```
86
+
87
+ ### Error Detection and Logging
88
+ Use try functions to detect errors, capture details, then handle the situation outside the try scope:
89
+
90
+ ```
91
+ // Pattern:
92
+ 1. Call try function
93
+ 2. If failed: capture GetLastErrorText()
94
+ 3. Log error or notify user
95
+ 4. Take appropriate action
96
+ ```
97
+
98
+ ## Best Practices
99
+
100
+ ### Keep Try Functions Focused
101
+ - Single responsibility: one external call or validation per try function
102
+ - Minimal code inside the try function
103
+ - No side effects beyond the intended operation
104
+
105
+ ### Handle Errors Appropriately
106
+ - Always check the return value of try function calls
107
+ - Use GetLastErrorText() to capture error details
108
+ - Provide meaningful feedback or logging
109
+
110
+ ### Separate Concerns
111
+ - Database operations: outside try functions
112
+ - External calls: inside try functions
113
+ - Error handling logic: after try function returns
114
+
115
+ ## Common Mistakes
116
+
117
+ ### Database Writes Inside Try
118
+ Attempting to modify, insert, or delete records inside a try function or its call stack will fail at runtime.
119
+
120
+ ### Ignoring Return Values
121
+ Calling a try function without checking its boolean return defeats the purpose of error handling.
122
+
123
+ ### Over-Reliance on Try Functions
124
+ Using try functions as a general error suppression mechanism rather than for specific, expected failure scenarios creates fragile code.
125
+
126
+ ## Related Patterns
127
+
128
+ For scenarios requiring transaction isolation with error handling, see the Codeunit.Run() pattern which provides proper transaction boundaries with error capture capabilities.
129
+
@@ -0,0 +1,201 @@
1
+ ---
2
+ title: "Partner Readiness Analysis for AppSource Apps"
3
+ domain: "morgan-market"
4
+ difficulty: "advanced"
5
+ bc_versions: "18+"
6
+ tags: ["appsource", "partner-readiness", "extensibility", "telemetry", "access-modifiers", "events", "isv"]
7
+ samples: "samples/partner-readiness-checklist.md"
8
+ related_topics:
9
+ - "../jordan-bridge/event-architecture.md"
10
+ - "../alex-architect/api-interface-design-patterns.md"
11
+ - "../dean-debug/telemetry-fundamentals.md"
12
+ - "../seth-security/access-modifier-strategy.md"
13
+ ---
14
+ # Partner Readiness Analysis for AppSource Apps
15
+
16
+ ## Overview
17
+
18
+ Publishing an app to AppSource involves more than passing technical validation. A truly partner-ready app considers the full lifecycle: how partners will extend it, how you'll support it in production, how customers will integrate with it, and how the codebase will evolve over time.
19
+
20
+ **Core Principle**: AppSource validation is the minimum bar. Partner readiness is the path to marketplace success and sustainable business.
21
+
22
+ ## The Partner Lifecycle Mindset
23
+
24
+ When you publish to AppSource, you're not just shipping code—you're entering a partnership ecosystem:
25
+
26
+ - **Partners (PTEs)** will build on top of your app
27
+ - **Customers** will integrate your app into their processes
28
+ - **You** will need to diagnose issues remotely
29
+ - **Microsoft** will evolve the platform under your app
30
+
31
+ Each of these relationships requires intentional design decisions that go beyond "does it work?"
32
+
33
+ ## Partner Readiness Checklist
34
+
35
+ ### 1. Event Architecture for Extensibility
36
+
37
+ **Why It Matters**: Partners and PTEs need to extend your app without modifying your code. Without proper events, they're stuck—or worse, they'll find workarounds that break on your next update.
38
+
39
+ **Key Questions**:
40
+ - [ ] Do critical business processes raise events before and after key operations?
41
+ - [ ] Can partners inject validation logic before you commit data?
42
+ - [ ] Can partners react to state changes without polling?
43
+ - [ ] Are events granular enough to be useful but not so numerous as to be noisy?
44
+
45
+ **What to Expose**:
46
+ - Document posting and state transitions
47
+ - Master data creation and modification
48
+ - Integration touchpoints (before/after external calls)
49
+ - Validation phases where partners might add business rules
50
+
51
+ **Anti-Patterns**:
52
+ - Raising events with insufficient context (missing key fields)
53
+ - Events that fire but can't influence the outcome
54
+ - Undocumented events that partners discover by accident
55
+
56
+ **Specialist Referral**: → **Jordan Bridge** for event architecture patterns and publisher/subscriber design
57
+
58
+ ---
59
+
60
+ ### 2. Telemetry for Production Support
61
+
62
+ **Why It Matters**: When something goes wrong at a customer site, you won't have debugger access. Telemetry is your eyes into production behavior—and your evidence when diagnosing "it doesn't work" reports.
63
+
64
+ **Key Questions**:
65
+ - [ ] Do you emit custom telemetry for key business operations?
66
+ - [ ] Can you identify which customer, which document, which operation from telemetry alone?
67
+ - [ ] Do you capture timing information for performance-sensitive operations?
68
+ - [ ] Are errors logged with enough context to diagnose without reproduction?
69
+
70
+ **What to Instrument**:
71
+ - External service calls (start, end, success/failure, duration)
72
+ - Long-running operations with intermediate checkpoints
73
+ - Error conditions with business context (not just technical stack traces)
74
+ - Configuration-dependent behavior branches
75
+
76
+ **Anti-Patterns**:
77
+ - Logging so verbose it becomes noise
78
+ - Missing correlation IDs across related operations
79
+ - Telemetry that exposes PII or sensitive business data
80
+
81
+ **Specialist Referral**: → **Dean Debug** for telemetry implementation patterns and KQL query strategies
82
+
83
+ ---
84
+
85
+ ### 3. Access Modifier Strategy
86
+
87
+ **Why It Matters**: Your `public` and `internal` decisions define your support surface. Everything marked `public` becomes a contract you must maintain. Everything marked `internal` gives you freedom to refactor.
88
+
89
+ **Key Questions**:
90
+ - [ ] Is every public procedure intentionally public, or just "default"?
91
+ - [ ] Are internal implementation details protected from external callers?
92
+ - [ ] Do codeunits have appropriate access modifiers (Public vs Internal)?
93
+ - [ ] Are procedures that partners SHOULD call clearly marked and documented?
94
+
95
+ **Strategic Decisions**:
96
+ - **Public Codeunits/Procedures**: Partner-facing API surface—document, version, maintain
97
+ - **Internal Codeunits/Procedures**: Implementation freedom—refactor without breaking partners
98
+ - **Protected Tables**: Control who can write to your data
99
+ - **Access = Public on Tables/Pages**: Required for extensibility, but increases your contract surface
100
+
101
+ **Anti-Patterns**:
102
+ - Making everything public "just in case"
103
+ - No documentation of what the public API actually is
104
+ - Changing internal behavior that partners somehow depended on
105
+
106
+ **Specialist Referral**: → **Seth Security** for access modifier strategy and encapsulation patterns
107
+
108
+ ---
109
+
110
+ ### 4. Interface Architecture
111
+
112
+ **Why It Matters**: Interfaces enable dependency inversion—partners can provide implementations you call without tight coupling. This is essential for apps that need pluggable behavior.
113
+
114
+ **Key Questions**:
115
+ - [ ] Do you use interfaces where partner-provided implementations make sense?
116
+ - [ ] Are interfaces well-documented with clear implementation contracts?
117
+ - [ ] Can partners register their implementations without modifying your code?
118
+ - [ ] Is interface discovery and registration straightforward?
119
+
120
+ **Good Interface Candidates**:
121
+ - External system connectors (shipping, payment, tax services)
122
+ - Calculation engines with customer-specific logic
123
+ - Document generation with format variations
124
+ - Notification handlers with channel flexibility
125
+
126
+ **Anti-Patterns**:
127
+ - Interfaces without documentation of expected behavior
128
+ - No default implementation for optional interfaces
129
+ - Registration mechanisms that require insider knowledge
130
+
131
+ **Specialist Referral**: → **Alex Architect** for interface design patterns and facade architecture
132
+
133
+ ---
134
+
135
+ ### 5. Upgrade and Migration Considerations
136
+
137
+ **Why It Matters**: Your app will evolve. Partners will have extensions depending on your schema and APIs. Breaking changes have real business impact.
138
+
139
+ **Key Questions**:
140
+ - [ ] Do you have a versioning strategy for your public API?
141
+ - [ ] Are breaking changes communicated before releases?
142
+ - [ ] Do upgrade codeunits handle data migration for schema changes?
143
+ - [ ] Is there a deprecation process for obsolete functionality?
144
+
145
+ **Best Practices**:
146
+ - Use `ObsoleteState` and `ObsoleteReason` to communicate deprecation timeline
147
+ - Provide upgrade codeunits for schema migrations
148
+ - Document breaking changes in release notes
149
+ - Give partners advance notice for significant changes
150
+
151
+ **Specialist Referral**: → **Logan Legacy** for upgrade codeunit patterns and migration strategies
152
+
153
+ ---
154
+
155
+ ### 6. Documentation for Partner Success
156
+
157
+ **Why It Matters**: Partners can't use what they can't discover. Documentation transforms your app from "code that works" to "platform partners can build on."
158
+
159
+ **Key Questions**:
160
+ - [ ] Are public APIs documented with usage examples?
161
+ - [ ] Are events discoverable with clear documentation of when they fire?
162
+ - [ ] Is there guidance for common extension scenarios?
163
+ - [ ] Do partners know how to get help when they're stuck?
164
+
165
+ **Documentation Assets**:
166
+ - API reference (procedures, parameters, return values)
167
+ - Event catalog (event, context, typical use cases)
168
+ - Integration guide (common partner scenarios)
169
+ - Troubleshooting guide (common issues and resolution)
170
+
171
+ **Specialist Referral**: → **Taylor Docs** for documentation structure and partner communication
172
+
173
+ ---
174
+
175
+ ## Analysis Workflow
176
+
177
+ When analyzing an app for partner readiness, work through each category systematically:
178
+
179
+ 1. **Inventory**: What events, public APIs, and telemetry currently exist?
180
+ 2. **Gap Analysis**: What's missing for each category based on the checklists?
181
+ 3. **Priority Assessment**: Which gaps have the highest partner impact?
182
+ 4. **Remediation Plan**: What changes are needed, and in what order?
183
+ 5. **Specialist Handoffs**: Which specialists should address specific gaps?
184
+
185
+ ## Continuous Improvement
186
+
187
+ Partner readiness isn't a one-time checklist—it's an ongoing discipline:
188
+
189
+ - **Gather Feedback**: Listen to partners using your app
190
+ - **Monitor Telemetry**: What operations cause the most issues?
191
+ - **Review Gaps**: After each release, what extension scenarios weren't supported?
192
+ - **Iterate**: Improve event coverage and documentation with each version
193
+
194
+ ## Summary
195
+
196
+ AppSource validation asks: "Does this app work?"
197
+
198
+ Partner readiness asks: "Can partners succeed with this app?"
199
+
200
+ The difference is the foundation of a sustainable AppSource business.
201
+
@@ -0,0 +1,288 @@
1
+ # Partner Readiness Checklist Examples
2
+
3
+ ## Event Architecture Review
4
+
5
+ ### Good: Well-Designed Event Pattern
6
+ ```al
7
+ codeunit 50100 "My Document Processor"
8
+ {
9
+ // Partners can validate before processing
10
+ [IntegrationEvent(false, false)]
11
+ local procedure OnBeforeProcessDocument(var MyDocument: Record "My Document"; var IsHandled: Boolean)
12
+ begin
13
+ end;
14
+
15
+ // Partners can react after processing
16
+ [IntegrationEvent(false, false)]
17
+ local procedure OnAfterProcessDocument(var MyDocument: Record "My Document"; Success: Boolean)
18
+ begin
19
+ end;
20
+
21
+ // Partners can add line-level logic
22
+ [IntegrationEvent(false, false)]
23
+ local procedure OnBeforeProcessDocumentLine(
24
+ var MyDocument: Record "My Document";
25
+ var MyDocumentLine: Record "My Document Line";
26
+ var IsHandled: Boolean)
27
+ begin
28
+ end;
29
+
30
+ procedure ProcessDocument(var MyDocument: Record "My Document")
31
+ var
32
+ IsHandled: Boolean;
33
+ begin
34
+ OnBeforeProcessDocument(MyDocument, IsHandled);
35
+ if IsHandled then
36
+ exit;
37
+
38
+ ProcessLines(MyDocument);
39
+ FinalizeDocument(MyDocument);
40
+
41
+ OnAfterProcessDocument(MyDocument, true);
42
+ end;
43
+ }
44
+ ```
45
+
46
+ ### Poor: Missing Event Context
47
+ ```al
48
+ // ❌ Partners can't do anything useful with this
49
+ [IntegrationEvent(false, false)]
50
+ local procedure OnProcess()
51
+ begin
52
+ end;
53
+
54
+ // ❌ No IsHandled - partners can't prevent default behavior
55
+ [IntegrationEvent(false, false)]
56
+ local procedure OnBeforePost(DocumentNo: Code[20])
57
+ begin
58
+ end;
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Telemetry Implementation
64
+
65
+ ### Good: Comprehensive Operation Telemetry
66
+ ```al
67
+ codeunit 50101 "External Service Connector"
68
+ {
69
+ var
70
+ TelemetryCategory: Label 'MyApp-ExternalService', Locked = true;
71
+
72
+ procedure CallExternalService(DocumentNo: Code[20]): Boolean
73
+ var
74
+ StartTime: DateTime;
75
+ Duration: Duration;
76
+ CustomDimensions: Dictionary of [Text, Text];
77
+ begin
78
+ StartTime := CurrentDateTime();
79
+
80
+ CustomDimensions.Add('DocumentNo', DocumentNo);
81
+ CustomDimensions.Add('Operation', 'CallExternalService');
82
+ CustomDimensions.Add('Endpoint', GetEndpointName());
83
+
84
+ Session.LogMessage(
85
+ 'MYAPP-0001',
86
+ 'External service call started',
87
+ Verbosity::Normal,
88
+ DataClassification::SystemMetadata,
89
+ TelemetryScope::ExtensionPublisher,
90
+ CustomDimensions);
91
+
92
+ if not TryCallService(DocumentNo) then begin
93
+ Duration := CurrentDateTime() - StartTime;
94
+ CustomDimensions.Add('Duration', Format(Duration));
95
+ CustomDimensions.Add('ErrorMessage', GetLastErrorText());
96
+
97
+ Session.LogMessage(
98
+ 'MYAPP-0002',
99
+ 'External service call failed',
100
+ Verbosity::Error,
101
+ DataClassification::SystemMetadata,
102
+ TelemetryScope::ExtensionPublisher,
103
+ CustomDimensions);
104
+ exit(false);
105
+ end;
106
+
107
+ Duration := CurrentDateTime() - StartTime;
108
+ CustomDimensions.Add('Duration', Format(Duration));
109
+
110
+ Session.LogMessage(
111
+ 'MYAPP-0003',
112
+ 'External service call completed',
113
+ Verbosity::Normal,
114
+ DataClassification::SystemMetadata,
115
+ TelemetryScope::ExtensionPublisher,
116
+ CustomDimensions);
117
+
118
+ exit(true);
119
+ end;
120
+ }
121
+ ```
122
+
123
+ ### Poor: Insufficient Telemetry
124
+ ```al
125
+ // ❌ No telemetry at all for external call
126
+ procedure CallExternalService(DocumentNo: Code[20]): Boolean
127
+ begin
128
+ exit(TryCallService(DocumentNo));
129
+ end;
130
+
131
+ // ❌ Telemetry without context
132
+ Session.LogMessage('MYAPP-0001', 'Service called', Verbosity::Normal,
133
+ DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher);
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Access Modifier Strategy
139
+
140
+ ### Good: Intentional Access Control
141
+ ```al
142
+ // Public codeunit - this is the partner-facing API
143
+ codeunit 50102 "My Document API"
144
+ {
145
+ Access = Public;
146
+
147
+ // Public procedure - documented contract for partners
148
+ procedure CreateDocument(CustomerNo: Code[20]): Code[20]
149
+ var
150
+ DocProcessor: Codeunit "My Document Processor Internal";
151
+ begin
152
+ exit(DocProcessor.CreateDocumentInternal(CustomerNo));
153
+ end;
154
+
155
+ // Public procedure - partners can call this
156
+ procedure GetDocumentStatus(DocumentNo: Code[20]): Enum "My Document Status"
157
+ begin
158
+ exit(GetStatusInternal(DocumentNo));
159
+ end;
160
+ }
161
+
162
+ // Internal codeunit - implementation details hidden from partners
163
+ codeunit 50103 "My Document Processor Internal"
164
+ {
165
+ Access = Internal;
166
+
167
+ // Internal procedure - free to refactor
168
+ procedure CreateDocumentInternal(CustomerNo: Code[20]): Code[20]
169
+ begin
170
+ // Implementation that can change without breaking partners
171
+ end;
172
+ }
173
+ ```
174
+
175
+ ### Poor: Unintentional Public Surface
176
+ ```al
177
+ // ❌ Everything public by default - massive support surface
178
+ codeunit 50104 "My Codeunit"
179
+ {
180
+ // All these become partner contracts accidentally
181
+ procedure DoTheThing()
182
+ procedure HelperMethod1()
183
+ procedure HelperMethod2()
184
+ procedure InternalCalculation()
185
+ procedure DebugOutput()
186
+ }
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Interface Architecture
192
+
193
+ ### Good: Well-Documented Interface Pattern
194
+ ```al
195
+ // Clear interface contract
196
+ interface "IShipping Provider"
197
+ {
198
+ /// <summary>
199
+ /// Calculates shipping rate for the given shipment parameters.
200
+ /// </summary>
201
+ /// <param name="Weight">Total weight in base unit of measure</param>
202
+ /// <param name="DestinationCountry">ISO country code</param>
203
+ /// <returns>Shipping cost in LCY</returns>
204
+ procedure CalculateRate(Weight: Decimal; DestinationCountry: Code[10]): Decimal;
205
+
206
+ /// <summary>
207
+ /// Creates a shipping label and returns the tracking number.
208
+ /// </summary>
209
+ /// <param name="ShipmentNo">The shipment document number</param>
210
+ /// <returns>Carrier tracking number</returns>
211
+ procedure CreateLabel(ShipmentNo: Code[20]): Text[50];
212
+ }
213
+
214
+ // Default implementation partners can reference
215
+ codeunit 50110 "Default Shipping Provider" implements "IShipping Provider"
216
+ {
217
+ procedure CalculateRate(Weight: Decimal; DestinationCountry: Code[10]): Decimal
218
+ begin
219
+ // Default flat-rate calculation
220
+ exit(Weight * 0.50);
221
+ end;
222
+
223
+ procedure CreateLabel(ShipmentNo: Code[20]): Text[50]
224
+ begin
225
+ // Default - no label creation
226
+ exit('');
227
+ end;
228
+ }
229
+
230
+ // Registration mechanism
231
+ table 50100 "Shipping Provider Setup"
232
+ {
233
+ fields
234
+ {
235
+ field(1; "Primary Key"; Code[10]) { }
236
+ field(2; "Provider"; Enum "Shipping Provider") { }
237
+ }
238
+ }
239
+
240
+ enum 50100 "Shipping Provider" implements "IShipping Provider"
241
+ {
242
+ Extensible = true;
243
+
244
+ value(0; Default)
245
+ {
246
+ Implementation = "IShipping Provider" = "Default Shipping Provider";
247
+ }
248
+ }
249
+ ```
250
+
251
+ ---
252
+
253
+ ## Partner Readiness Summary Report Template
254
+
255
+ ```al
256
+ // Generate a readiness report for your app
257
+ codeunit 50199 "Partner Readiness Report"
258
+ {
259
+ procedure GenerateReport()
260
+ var
261
+ Report: TextBuilder;
262
+ begin
263
+ Report.AppendLine('# Partner Readiness Report');
264
+ Report.AppendLine('Generated: ' + Format(CurrentDateTime()));
265
+ Report.AppendLine('');
266
+
267
+ Report.AppendLine('## Event Coverage');
268
+ Report.AppendLine('- Integration Events: ' + Format(CountIntegrationEvents()));
269
+ Report.AppendLine('- Business Events: ' + Format(CountBusinessEvents()));
270
+ Report.AppendLine('');
271
+
272
+ Report.AppendLine('## Access Modifier Summary');
273
+ Report.AppendLine('- Public Codeunits: ' + Format(CountPublicCodeunits()));
274
+ Report.AppendLine('- Internal Codeunits: ' + Format(CountInternalCodeunits()));
275
+ Report.AppendLine('');
276
+
277
+ Report.AppendLine('## Telemetry Coverage');
278
+ Report.AppendLine('- LogMessage Calls: ' + Format(CountTelemetryCalls()));
279
+ Report.AppendLine('');
280
+
281
+ Report.AppendLine('## Interface Definitions');
282
+ Report.AppendLine('- Interfaces: ' + Format(CountInterfaces()));
283
+ Report.AppendLine('- Implementations: ' + Format(CountImplementations()));
284
+
285
+ Message(Report.ToText());
286
+ end;
287
+ }
288
+ ```
@@ -0,0 +1,82 @@
1
+ ---
2
+ title: "Isolation Testing Patterns with Test Doubles"
3
+ domain: "quinn-tester"
4
+ difficulty: "advanced"
5
+ bc_versions: "18+"
6
+ tags: ["testing", "isolation", "test-doubles", "mocks", "spies", "dependency-injection", "unit-testing"]
7
+ samples: "samples/isolation-testing-patterns.md"
8
+ related_topics:
9
+ - "../alex-architect/testability-design-patterns.md"
10
+ - "../roger-reviewer/testability-code-smells.md"
11
+ source: "Adapted from Vjeko.com: Testing in isolation (Dec 2023)"
12
+ ---
13
+ # Isolation Testing Patterns with Test Doubles
14
+
15
+ ## Overview
16
+
17
+ Testing in isolation means testing your code independent from its dependencies. When code is designed with interfaces and dependency injection, you substitute real implementations with "test doubles"—fake implementations giving complete control during testing.
18
+
19
+ **Core Principle**: Test YOUR code, not your dependencies. Use test doubles to isolate what you're testing from what you're depending on.
20
+
21
+ ## Types of Test Doubles
22
+
23
+ ### Dummy
24
+ A placeholder passed but never used. Satisfies parameter requirements when the dependency isn't exercised in your test path.
25
+
26
+ ### Stub
27
+ Returns predetermined values. Provides canned answers to calls made during test. Use when you need the dependency to return specific values.
28
+
29
+ ### Spy
30
+ Records what happened during execution. Allows assertions about how dependencies were called—was it invoked? With what parameters?
31
+
32
+ ### Mock
33
+ Pre-programmed with expectations. Controls dependency behavior AND verifies interaction patterns.
34
+
35
+ ### Throwing Double
36
+ Simulates errors. Use for testing error handling paths when dependencies fail.
37
+
38
+ ## When to Use Each
39
+
40
+ | Scenario | Double | Why |
41
+ |----------|--------|-----|
42
+ | Dependency not used in test path | Dummy | Just satisfies signature |
43
+ | Need specific return value | Stub | Control the response |
44
+ | Need to verify it was called | Spy | Record and inspect |
45
+ | Need to control AND verify | Mock | Full control |
46
+ | Dependency might throw errors | Throwing | Test error handling |
47
+
48
+ ## Testing Patterns
49
+
50
+ **Happy Path**: Permission granted, conversion succeeds, logging occurs. Use stub for converter, spy for logger.
51
+
52
+ **Permission Denial**: Permission denied, process fails before conversion. Use dummy converter (won't be called), spy to verify no logging.
53
+
54
+ **Parameter Verification**: Verify dependencies receive correct parameters. Use spy that captures arguments for assertion.
55
+
56
+ **Error Propagation**: Converter fails, error propagates correctly. Use throwing double, verify logger not invoked.
57
+
58
+ ## Structuring Test Doubles
59
+
60
+ **Naming**: `[Type] [Interface Name]` - e.g., "Spy Logger", "Stub Converter"
61
+
62
+ **Organization**: Keep test doubles in test app, organized by interface they implement.
63
+
64
+ ## Test Production Implementations Separately
65
+
66
+ Test your actual implementations (database permission checker, database logger) directly with simple, focused tests. Then use test doubles when testing business logic that USES those implementations.
67
+
68
+ ## Benefits
69
+
70
+ - **Speed**: No database = fast tests (hundreds in seconds)
71
+ - **Reliability**: No external dependencies = no flaky tests
72
+ - **Focus**: Test exactly what you mean to test
73
+ - **Maintainability**: Dependency changes don't break business logic tests
74
+
75
+ ## When You Still Need Integration Tests
76
+
77
+ Isolation tests verify your code works given certain dependency behaviors. Integration tests verify dependencies are wired correctly and end-to-end scenarios function.
78
+
79
+ **Rule**: Many isolation tests, few integration tests. The pyramid, not the ice cream cone.
80
+
81
+ See samples for complete test double implementations and test codeunit examples.
82
+