bcquality/microsoft/knowledge/testing/bcpt-scenarios-must-be-app-specific.good.al
Michael Dieringer 4a95985c8c Fix four merge-critical issues from Jesper's 2026-09-22 review
- pages-must-not-contain-business-logic.good.al/.bad.al: the "good"
  codeunit still directly assigned real Sales Line."Line Amount" and
  called Modify(), bypassing the field's normal Validate cascade
  (discount, VAT, related-amount maintenance) - persisting inconsistent
  document lines regardless of which object the code lived in. Replaced
  the real Sales Line example with a self-contained "Sample Order Line"
  table and switched the codeunit to Validate()/Modify(true), so the
  fixture demonstrates the page-vs-codeunit separation without teaching
  unsafe direct field writes to a real BC document table.
- bcpt-scenarios-must-be-app-specific.good.al: Customer.FindFirst()
  assumed a pre-existing customer (fails against an empty environment),
  and a session-local NextNo counter for the header key collides across
  concurrent BCPT sessions and repeated runs. Creates its own customer
  when none exists, and generates keys from CreateGuid() instead of an
  in-memory counter.
- upgrade-tag-logic-must-not-nest-deeply: the rule conflated two
  different things - nesting one tag's existence check inside another
  (the real anti-pattern Microsoft's guidance warns against) with
  having business-data safety conditions inside a single tagged
  migration's own loop body (which Microsoft's own worked example does,
  and its own design guidance explicitly requires: "Implement extra
  safety checks to avoid data corruption, even though you're using
  upgrade tags"). Rewrote the Description/Best Practice/Anti Pattern to
  scope the rule to actual tag nesting and migrations blended under one
  tag, and rewrote both fixtures: good.al now shows two safety
  conditions correctly nested inside one migration's own loop plus a
  second, genuinely separate migration as its own flat tagged
  procedure; bad.al now shows the real anti-pattern, one tag's check
  nested inside another's guarded body.
- table-design-must-match-bc-table-type-conventions: the rule and its
  worklist cue fired on any new table with a keys block, forcing
  buffers, queues, logs, mapping tables, and staging tables into the
  nearest-looking one of nine business-record archetypes. Added an
  explicit scope note that these nine types aren't an exhaustive table
  catalogue, and narrowed the al-data-modeling-review.md cue to require
  positive evidence (a type-specific naming suffix, key shape, or
  usage) before worklisting, instead of a bare keys/primary-key
  declaration.
2026-09-22 14:41:49 +02:00

73 lines
2.6 KiB
AL

codeunit 50100 "BCPT Create Service Request" implements "BCPT Test Param. Provider"
{
SingleInstance = true;
trigger OnRun()
begin
if not IsInitialized then begin
InitTest();
IsInitialized := true;
end;
CreateServiceRequest(GlobalBCPTTestContext);
end;
var
GlobalBCPTTestContext: Codeunit "BCPT Test Context";
CustomerNo: Code[20];
IsInitialized: Boolean;
local procedure InitTest()
var
Customer: Record Customer;
begin
// Do not assume a customer already exists: a BCPT run may target an
// otherwise-empty environment. Create one if none is found instead
// of failing on FindFirst().
if not Customer.FindFirst() then begin
Customer.Init();
Customer."No." := GenerateUniqueCode(MaxStrLen(Customer."No."));
Customer.Insert(true);
end;
CustomerNo := Customer."No.";
end;
local procedure GenerateUniqueCode(Length: Integer): Code[20]
begin
// A GUID-derived code, not a session-local counter: it stays unique
// across concurrent BCPT sessions and repeated runs against the
// same environment, which an in-memory counter reset per session
// cannot guarantee.
exit(CopyStr(DelChr(Format(CreateGuid()), '=', '{}-'), 1, Length));
end;
local procedure CreateServiceRequest(var BCPTTestContext: Codeunit "BCPT Test Context")
var
ServiceRequestHeader: Record "Service Request Header";
ServiceRequestLine: Record "Service Request Line";
begin
BCPTTestContext.StartScenario('Create Service Request Header');
ServiceRequestHeader.Init();
ServiceRequestHeader."No." := GenerateUniqueCode(MaxStrLen(ServiceRequestHeader."No."));
ServiceRequestHeader.Validate("Customer No.", CustomerNo);
ServiceRequestHeader.Insert(true);
BCPTTestContext.EndScenario('Create Service Request Header');
BCPTTestContext.UserWait();
BCPTTestContext.StartScenario('Add Service Request Line');
ServiceRequestLine.Init();
ServiceRequestLine."Document No." := ServiceRequestHeader."No.";
ServiceRequestLine."Line No." := 10000;
ServiceRequestLine.Description := 'Performance test line';
ServiceRequestLine.Insert(true);
BCPTTestContext.EndScenario('Add Service Request Line');
end;
procedure GetDefaultParameters(): Text[1000]
begin
exit('');
end;
procedure ValidateParameters(Parameters: Text[1000])
begin
end;
}