Skip to content

test(wfs): WFS read/write parity characterization tests (Decisions 1–9) run as a real manager persona - #377

Open
overfullstack wants to merge 61 commits into
masterfrom
wfs/decision-1-9-revoman-tests
Open

test(wfs): WFS read/write parity characterization tests (Decisions 1–9) run as a real manager persona#377
overfullstack wants to merge 61 commits into
masterfrom
wfs/decision-1-9-revoman-tests

Conversation

@overfullstack

Copy link
Copy Markdown
Contributor

What

Off-core ReVoman integration tests that characterize live Salesforce 262 Workforce Scheduling behavior across six read/write parity decisions about how a non-required "helper" resource is treated (plus a read-path result cap and a shift-sharing gate). Each test asserts what 262 actually does today — including two genuine server crashes — with the 264 expectation documented per scenario, so the suite flips and alerts when 264 changes a behavior.

Two test classes supersede the old WfsHelperFitnessE2ETest / WfsDoubleBookHelperE2ETest:

  • WfsWritePathParityE2ETest — Decisions 1 (4 dims), 1.4, 1.5, 3.
  • WfsReadPathParityE2ETest — Decisions 8, 9.

The headline of the latest commit: real manager persona

The API-under-test calls for Decisions 1/1.4/1.5/3/8 now run as a real least-privilege Workforce Scheduling manager (its own SOAP session) instead of an admin-token alias — matching Decision 9, which always used real personas. The manager owns the policies + fixtures it books/reads against (a manager can't see another user's private shifts, so booking against admin-owned data would hit the very sharing gate Decision 9 is about).

  • auth/login-as-sysadmin no longer aliases managerToken=adminToken; it sets admin/access only and unsets managerToken so a broken manager login fails loud (no silent admin fallback).
  • New auth/set-manager-password + auth/login-as-manager (v64 SOAP) mint the real manager session after create-manager.
  • The 4 straggler acts now use {{managerToken}} (2 Decision-8 + 2 Decision-3). skills-non-required-skill stays {{adminToken}} (Skill is a setup object the manager can't create).

Decisions verified (live, as the manager persona — every verdict unchanged)

Decision Scenario 262 result
1 helper not fitness-checked (excluded / territory / skills / working-locations) all Success
1.4 helper can't satisfy required demand server crash (serviceTerritoryMembers NPE); control not-RequiredResources
1.5 helper double-books Success; required control rejected
3 missing isRequiredResource flag server crash (Boolean.booleanValue NPE); L142 control Success
8 resourceLimitApptDistribution cap limit 0 → empty + no error; limit 50 → resources
9 shift sharing-mode split manager (owner) → slots; case-worker (no sharing) → 0 slots, no error

The two crashes (1.4, 3) are known 262 product defects, pinned down verbatim so the suite alerts the moment 264 fixes them.

Notes for reviewers

  • Tests are enabled but environment-gated. They need a provisioned WFS workspace org + external-org creds and are meant to be run manually against the workspace org — they will not pass in unattended CI. The full preconditions list is in the test plan.
  • Test plan: docs/superpowers/2026-06-30-wfs-parity-test-report.md documents every scenario (setup → rule isolated → exact assertion → recorded result) plus honest coverage gaps.
  • Also includes agent-readable descriptions on the collection folders, two stale-description fixes, and an auth-id de-dup. No production code changed; test logic untouched.

@overfullstack
overfullstack force-pushed the wfs/decision-1-9-revoman-tests branch 3 times, most recently from 3078406 to 5f9e635 Compare July 1, 2026 05:40
… admin alias)

The WFS parity acts already declared {{managerToken}}, but auth/login-as-sysadmin
aliased managerToken=adminToken, so every API-under-test call ran as admin. Mint a
real least-privilege WorkforceScheduling manager session instead and make it own the
policies + fixtures it books/reads against, matching Decision 9.

- auth/login-as-sysadmin: stop aliasing managerToken; set admin/access only and
  unset managerToken so a broken manager login fails loud (no silent admin fallback).
- New auth/set-manager-password (17500) + auth/login-as-manager (18000, v64 SOAP)
  after create-manager (17000): the manager gets its own session token.
- Point the 4 straggler acts at {{managerToken}} (2 Decision-8 get-available-resources
  + 2 Decision-3 schedule). skills-non-required-skill stays {{adminToken}} (Skill is a
  setup object the manager can't create).
- Doc-only: fix two stale collection descriptions, de-dup an auth id, refresh the
  AUTH_CONFIG javadoc, and add agent-readable descriptions to the collection folders.
- Enable both test classes (run manually against a provisioned WFS workspace org).
- Add the WFS parity test plan under docs/superpowers.

All 10 scenarios re-verified live as the manager persona; every verdict unchanged
(behaviors are caller-independent). Test logic untouched.
Investigation-grounded (5 read-only Core probes): 4=clean INVALID_INPUT
input-validation reject; 4z=doc is WRONG (reschedule CAN leave no primary;
the 'isPrimaryResource cannot be set for Delete' error fires only when a
DeleteOperation entry carries the flag); 5=persist INVALID_FIELD reject, no
auto-correct, no double-book; 2=field-match half not characterizable on 262
(OnField/ESO black box, OnSite shares read==write) so test the verified
cheap-check promise.
…s doc)

Arm A: isPrimaryResource on a DeleteOperation entry -> INVALID_INPUT/400
'isPrimaryResource cannot be set for delete.' (validateDeleteOperationFields
payload-field guard). Arm B: delete the primary, no flag -> the primary-count
validator allows zero primaries (validatePrimaryResourceCount only throws
MultiplePrimary when primaryCount > 1), so there is NO no-primary rule -- which
refutes the product doc. LIVE-OBSERVED on the 262 org: Arm B is rejected only by
a downstream availability re-check (INVALID_INPUT 'The service resources are not
available for the requested slot.' / SlotNotAvailable), never by a primary rule;
262 lacks the empty/no-op-reschedule availability short-circuit added in 264
(precommit 58140158). Characterized faithfully (availability, not no-primary)
rather than forcing a Success the 262 org cannot produce. Clean two-resource
schedule captures reschedCleanSaId for both arms.
…roofs)

All 4 read APIs (get-slots/candidates/available-slots/available-resources) +
2 write APIs (schedule/reschedule) share the loadSchedulableSlots engine over
the 7 Common+InBusiness rules. Differential agreement matrix asserts read==write
per rule + surfaces the 2 genuine divergences (reschedule no-op short-circuit;
RequiredResources 262 NPE). onField/inField rules out of scope. jdwp double-assert
where API verdict is ambiguous.
…v carry

ReVoman.revUp accumulates env between kicks via mutableEnvCopyWithValuesOfType<String>()
(ReVoman.kt:96) — only String values survive kick→kick. Four afterResponse scripts stored
counts as raw JS numbers, so a count set in a non-last kick was dropped before the final
rundown; getAsString returned null → testResourceLimitApptDistributionCapE2E failed
(expected: 0 but was: null). Coerce with String(...) to match the parityRead* sibling idiom;
also fixes the latent break in testShiftSharingModeSplitE2E (dec9Manager/CaseWorkerSlotCount).

Verbose integrationTest logging: log4j2 com.salesforce.revoman → DEBUG (override with
-Dlog.revoman.level=INFO; okhttp3/graalvm pinned WARN) + testLogging.showStandardStreams so
the per-step execution trace streams live instead of burying in the stdout capture file.
Lift visiting-hours-op-hours-policy + visiting-hours-account-oh from source and conform to 262: strip WorkType SchedulingMethod/IsRegular, add ShiftUsage=Union param, link Account.OperatingHoursId via an admin-token PATCH step (WorkforceSchedulingManager has read-only FLS on that field). 4 acts prove a window OUTSIDE the account visiting hours (10-14) is pruned read (0 slots) AND rejected write, while an in-hours control returns >0 AND Success. Live PASS on 262.
Signed-off-by: Gopal S Akshintala <gopala.akshintala@salesforce.com>
…gine NPE)

Plan hypothesized a read-prunes/write-crashes DIVERGENCE; live evidence refutes
it. The READ path (GetAppointmentSlots) crashes with the SAME serviceTerritoryMembers
NPE (HTTP 500 INTERNAL_SERVER_ERROR) the write path throws, because both share
InBusinessGetCandidatesSlotsDataService.loadSchedulableSlots — so the 262
RequiredResources bug is read==write (a shared-engine crash), NOT a divergence.
Control (resourceB required → demand satisfied) does not crash (HTTP 201, 0 slots),
proving the crash is conditional on the non-required-helper input.
…valuating APIs)

One MatchSkills violation (a required resource lacking the WorkType's skill) run through all
4 rule-evaluating read APIs (get-appointment-slots/candidates/available-slots/available-resources)
plus the schedule write, asserting unanimous pruning/rejection — empirically proving they share the
one loadSchedulableSlots engine so the per-rule read==write matrix generalizes to every API.

Discovered live (2026-07-01):
- get-appointment-candidates + get-available-slots return result[].candidates[] (not slots[]);
  get-available-slots with assignedResources dispatches to get-appointment-slots then converts.
- get-appointment-candidates rejects isPrimaryResource in assignedResources (INVALID_INPUT
  'isPrimaryResource is not supported.') so its body keeps only isRequiredResource; MatchSkills
  then prunes to 0 candidates (201).
- get-available-resources runs the FULL 7-rule engine (getCandidatesProcessor.process): it takes
  no assignedResources and returns EVERY available resource, so the skilled resourceA survives
  while the unskilled resourceB is pruned/ABSENT — confirming it is NOT a rule-skipping subset.

Reuses the Task-1 skills fixture/policy + the existing get-slots/schedule violating acts.
…recompute NPE)

Plan premise (no-op reschedule returns Success via SlotAvailabilityChecker:174-176
short-circuit) REFUTED by live+jdwp evidence. For a required-resource SA the
short-circuit's resourcesHaveChanged is ALWAYS true — the existing SA's required
ServiceResourceId set (18-char from SOQL) never equals the request's (15-char after
the ESO request DTO truncation), so the branch is unreachable over REST. The
reschedule then recomputes and 262 crashes HTTP 500 INTERNAL_SERVER_ERROR
(ServiceTerritory.getServiceResourceIds() NPE) — a third 262 reschedule-recompute
NPE variant. Test asserts the observed 500/NPE, not the refuted Success premise.
Each Decision-N scenario in the parity report now names the exact test
method that pins it (WfsWritePathParityE2ETest / WfsReadPathParityE2ETest),
plus a Test-method column in the summary table. Several doc scenarios
(1a-d, A/B control pairs) collapse into a single method with multiple
dimensions/arms; the inline tags make that mapping explicit.
…ash pinned, 15/18 cause open)

Review flagged the 15-char-truncation mechanism claim as unverified and in tension
with the fixture's 18-char wire id (0Hnxx...FrgCAE is 18 chars). The CRASH (HTTP 500
getServiceResourceIds NPE) is live-proven and remains asserted; the WHY-short-circuit-
didn't-fire mechanism is downgraded to a flagged-open jdwp question. Assertions unchanged.
…t-circuit unreachable)

jdwp breakpoint at SlotAvailabilityChecker:237 on a live no-op reschedule: existing
SA required-resource id set = [0Hnxx0000004GB2CAM] (18-char, SOQL) vs request set =
[0Hnxx0000004GB2] (15-char, ESO request DTO truncation) → equals=false →
resourcesHaveChanged=true → short-circuit skipped → recompute → 262 crash. Confirms
the mechanism (was softened to open); the repo's recurring 15/18-char ResourceId
gotcha surfacing in the reschedule short-circuit comparison.
…4/4z/5 + rules-parity suite)

Adds the WfsRulesParityE2ETest suite (8 tests) + Decisions 2/4/4z/5 to the report;
records the headline (read==write holds, both predicted divergences refuted;
get-available-resources = full engine not subset), the 3 262 slot-gen NPE bugs +
4z doc bug (handed off), and the jdwp-confirmed 15/18-char no-op-reschedule mechanism.
Refreshes the at-a-glance table (28 rows) and coverage notes.
…adability review)

Per readability review: added a 1-minute summary + plain-language glossary (defines
262/264, read/write, characterization test, crash vs rejected, etc.); de-jargoned the
at-a-glance table into plain outcomes with a note that green = 'faithfully recorded'
(not 'healthy', since some rows are pinned crashes); replaced code identifiers, error
codes, and logic symbols (⟺/≠/==) throughout with plain English; unpacked the
15/18-char record-ID mechanism in words. Test-method names kept as code-font anchors
for engineers. Content unchanged — readability only.
…n, highlight 4z deviation

New section compares our live tests to the team's earlier manual observations (fetched
from the shared docs): 4 and 5 CONFIRMED; 2 confirmed in part (field-match half not
testable on 262); 4z is the one real DEVIATION — the observation's 'cannot reschedule
without a primary' conclusion is wrong (validation allows zero primaries; the quoted
error is a narrow field rule; the real stopper is an availability check) = doc bug.
Also renamed all 'product doc' references to 'manual observations from the team' per
user (these are team notes, not product docs).
…is on 262

New reschedule-delete-all act deletes BOTH assigned resources (no isPrimaryResource
flag) to probe whether an empty-crew reschedule gets around the availability check and
leaves a no-primary appointment (the end-to-end 4z proof Arm B could not deliver).

LIVE-OBSERVED on 262: it does NOT. The delete-all reschedule is rejected with the SAME
downstream availability re-check (INVALID_INPUT / HTTP 400 / SlotNotAvailable 'The service
resources are not available for the requested slot.'), schedulingStatus null — no Success,
no no-primary SA. The empty-effective-set short-circuit the Core func test
testRescheduleAppointmentDeleteAllAssignedResources relies on is 264-only. Characterized
faithfully as the observed rejection rather than a forced Success. New test method
testRescheduleDeleteAllLeavesNoPrimaryE2E passes live.
…a-layer detail + Dec-9 sharing tension

- 4z CORRECTION: earlier draft said 'delete every worker' gets around the availability
  check today — live test (testRescheduleDeleteAllLeavesNoPrimaryE2E) proves it does NOT
  on 262; all 3 removal paths are blocked by the same availability check. The empty-crew
  short-circuit is 264-only. Doc's conclusion = right outcome today, wrong reason, wrong
  next release. Added 4z-C table row.
- Dec 5: added where the refusal happens — a DATA-LAYER save hook on the AssignedResource
  entity (fires on every write path, not just this API), gated by the multi-resource org pref.
- Dec 9: added the case-worker tension — no read/write contradiction (can't book what you
  can't see; write reuses the same reads); the real issue is half-applied sharing within one
  operation. Added both product options (surface a reason / align the reads).
…ary is BLOCKED by availability (not crash, not persist)

The under-guarded ≥2-workers-ZERO-primary shape the sibling delete arms never build
(they shrink the crew to 1 or 0). New reschedule-demote-primary-two-crew act reschedules
the clean two-resource SA with NO time change and TWO UpdateOperation entries flipping
BOTH resourceA and resourceB to isPrimaryResource=false (isRequiredResource=true kept):
effective crew = 2 workers, 0 primaries.

LIVE-OBSERVED on 262: NOT Success and NOT a crash — rejected by the SAME downstream
availability re-check (SlotNotAvailable) that blocks the delete-primary Arm B and the
delete-ALL probe: INVALID_INPUT / HTTP 400 / top-level 'The service resources are not
available for the requested slot.', schedulingStatus null. Does NOT trip the 15/18-char
record-id resourcesHaveChanged bug (availability rejects first) and does NOT persist a
no-primary crew. Doc still refuted at the validation layer (validatePrimaryResourceCount
allows zero primaries); the availability rejection is a separate 262-only downstream gap
(264's effective-set merge would find the surviving-crew slot). Asserted as OBSERVED.
…ilability, not crash/persist)

testRescheduleDemotePrimaryTwoCrewNoPrimaryE2E: demote-to-2-workers-no-primary on 262
is BLOCKED by the availability check (HTTP 400 SlotNotAvailable) — corrects the earlier
reasoned guess (crash). No no-primary crew persists today; the gap is that nothing GUARDS
it (only availability blocks it), so the next-release final-crew rule is still needed.
…orrections

Relocated from the scheduler-parity branch where a git add -A had inadvertently
swept this WFS work. Adds testRescheduleDeleteAllWithPolicySucceedsE2E +
reschedule-delete-all-with-policy collection, and 264-contrast doc corrections
across the WFS parity report + test javadocs.
@overfullstack
overfullstack force-pushed the wfs/decision-1-9-revoman-tests branch from 4575c0e to 8f1f8da Compare July 6, 2026 07:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant