Skip to content

test(fga): live coverage for dry-run, ABAC and cache routing - #350

Open
yosiharan wants to merge 8 commits into
mainfrom
test/fga-live-coverage
Open

test(fga): live coverage for dry-run, ABAC and cache routing#350
yosiharan wants to merge 8 commits into
mainfrom
test/fga-live-coverage

Conversation

@yosiharan

@yosiharan yosiharan commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Related Issues

Verifies: https://github.com/descope/etc/issues/17574

Related PRs

Upstream PRs

In a Nutshell

  • Ports integrationtests/tests/fga_test.go to the Java SDK
  • GDrive schema round trip + all 17 check expectations
  • ABAC context cases, including missingContext
  • Dry-run and ABAC-through-authz
  • Cache routing covered positively and negatively

Description

Adds FGALiveTest, live coverage for everything the first three PRs added, in the harness the repo already uses (TestUtils.getClient() + @RetryingTest on rate limits, which CI runs with the project secrets).

Ported from integrationtests/tests/fga_test.go: runFGASchemaTest's GDrive schema, its 9 relations and all 17 check expectations, and TestFGAABACCheckWithContext's five assertions (conditional allow, conditional deny, missing context, and a condition on a permission). New on the Java side: dry-run, ABAC context through whoCanAccess / whatCanTargetAccess, and loadSchema returning version and conditions.

Cache routing gets three tiers, because CI cannot reach an authzcache instance:

  1. Cache URL pointed at the API host — the whole round trip must still pass, which exercises config -> client -> getFgaUri including trailing-slash normalization.
  2. Cache URL pointed at a dead port — the six routed calls must fail and the non-routed calls on the same client must still succeed. The pair is what proves the routed set is exactly those six.
  3. A real cache when DESCOPE_FGA_CACHE_URL is set, skipped otherwise.

Last of four stacked PRs — based on #349.

Must

  • Tests
  • Documentation (if applicable)

Note for reviewers

Schemas are saved per-test case and not deleted to avoid clashes with concurrent running CI that all share the same test project ID.

@yosiharan

Copy link
Copy Markdown
Contributor Author

@shuni review

@shuni-bot

shuni-bot Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🐕 Review complete — View session on Shuni Portal 🐾

@shuni-bot shuni-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐕 Shuni's Review

Adds FGALiveTest — GDrive schema round trip, ABAC context, dry-run and the three-tier FGA cache routing coverage. I traced all 17 GDrive expectations against the DSL (all correct), and the 6 routed calls in the negative test match exactly the getFgaUri call sites in FGAServiceImpl/AuthzServiceImpl. Good bones! 🦴

Sniffed out 2 issues:

  • 1 🟡 MEDIUM: @AfterAll leaves the shared project with no schema, which another live suite dereferences unguarded
  • 1 🟢 LOW: assertNotNull on a wire-nullable list

See inline comments for details. Woof!


Declared coverage: FULL — 1/1 changed files reviewed.

Comment thread src/test/java/com/descope/sdk/mgmt/impl/FGALiveTest.java Outdated
Comment thread src/test/java/com/descope/sdk/mgmt/impl/FGALiveTest.java Outdated
@yosiharan

Copy link
Copy Markdown
Contributor Author

@shuni review

@shuni-bot

shuni-bot Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🐕 Review complete — View session on Shuni Portal 🐾

@shuni-bot shuni-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐕 Shuni's Review

FGALiveTest ports the Go GDrive/ABAC coverage and adds dry-run plus the three-tier cache-routing tiers. Re-traced all 17 GDrive expectations against the DSL (correct), and the 7 assertThrows map exactly onto the 6 getFgaUri call sites. The previous round's two findings are addressed — no schema deletion, assertDoesNotThrow on the non-routed authz call. Good bones! 🦴

Sniffed out 3 issues:

  • 1 🟡 MEDIUM: retrying on ServerCommonException makes deterministic failures cost ~30 min across the matrix
  • 2 🟢 LOW: nullable targets on the deny-path whoCanAccess; two byte-identical cache round-trip tests

See inline comments for details. Woof!


Declared coverage: FULL — 1/1 changed files reviewed.

Comment thread src/test/java/com/descope/sdk/mgmt/impl/FGALiveTest.java
Comment thread src/test/java/com/descope/sdk/mgmt/impl/FGALiveTest.java Outdated
Comment thread src/test/java/com/descope/sdk/mgmt/impl/FGALiveTest.java
@yosiharan

Copy link
Copy Markdown
Contributor Author

@shuni review

@shuni-bot

shuni-bot Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🐕 Review complete — View session on Shuni Portal 🐾

@shuni-bot shuni-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐕 Shuni's Review

Third pass on FGALiveTest. Both prior LOWs are fixed (assertRoundTripWorks now shared, deny-path whoCanAccess null-guarded), and the retry cost concern is materially addressed — suspendForMs is 10s and AssertionFailedError is an Error, so assertion failures were never retried in the first place; only a genuine ServerCommonException costs ~20s per test.

Re-verified this round: all 5 model constructor arg orders against the Lombok field orders, the 17 GDrive expectations against the DSL, and the 7 assertThrows against the 6 getFgaUri call sites. getConditions() is guaranteed non-null by FGAServiceImpl.loadSchema, so line 198 is safe. Java 8 leg, checkstyle's 120-col limit and surefire's *Test pattern all clear. Good bones! 🦴

Sniffed out 1 issue:

  • 1 🟢 LOW: one assertion in the ABAC-through-authz block passes on an empty result

See inline comments for details. Woof!


Declared coverage: FULL — 1/1 changed files reviewed.

Comment thread src/test/java/com/descope/sdk/mgmt/impl/FGALiveTest.java Outdated
@yosiharan

Copy link
Copy Markdown
Contributor Author

@shuni review

@shuni-bot

shuni-bot Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🐕 Review complete — View session on Shuni Portal 🐾

@shuni-bot shuni-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐕 Shuni's Review

Fourth pass on FGALiveTest. The one open LOW is genuinely fixed: reachesDoc1 now contrasts role=admin against role=user through whatCanTargetAccess, so the assertion would fail if the server ignored context — and the helper null-guards the wire list.

Re-verified independently this round: all 17 GDrive expectations against the DSL, the 5 model constructor arg orders against their Lombok field orders, the 7 assertThrows against the exactly-6 getFgaUri call sites (check accounts for two), mapOf returns a mutable HashMap so the request.put("context", ...) paths in AuthzServiceImpl are safe, TestUtils.getClient() builds a fresh Client per call so setFgaCacheUri can't leak between tests, getInfo()/getMissingContext()/getConditions() are all impl-guaranteed non-null, and connection-refused surfaces as an IOException through @SneakyThrows so assertThrows(Exception.class, ...) catches it. Checkstyle doesn't scan test sources, so the 120-col limit is moot here anyway.

No issues sniffed out this round — nothing actionable survived verification. Good bones! 🦴

Woof!


Declared coverage: FULL — 1/1 changed files reviewed.


Declared coverage: FULL — 1/1 changed files reviewed.

@yosiharan yosiharan left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self review done, test covers ABAC and FGA Cache paths very nicely, ran manually to verify it passes when hitting a real env

@yosiharan
yosiharan marked this pull request as ready for review August 10, 2026 08:30
@yosiharan
yosiharan requested review from itaihanski and orius123 and removed request for itaihanski August 10, 2026 11:55
@yosiharan
yosiharan force-pushed the test/fga-live-coverage branch from e920006 to 76ca0cb Compare August 10, 2026 12:10
@shuni-bot

shuni-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🐕 Review complete — View session on Shuni Portal 🐾

@shuni-bot shuni-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐕 Shuni's Review

Adds FGALiveTest — the GDrive schema round trip, ABAC context cases, dry-run, and the three-tier FGA cache-routing coverage, on the repo's existing TestUtils.getClient() + @RetryingTest harness. The previously reviewed commit was force-pushed away, so I re-reviewed the full file rather than a delta.

Traced this round: all 17 GDrive expectations against the DSL's can_create/can_edit chains (including the group#member userset on folder2) — all correct; the 7 assertThrows map onto exactly the 6 getFgaUri call sites, with the non-routed calls on the same client asserted to still succeed, which is what makes the negative tier meaningful; the 5 model constructor arg orders against their Lombok field orders; mapOf returns a mutable HashMap, so the request.put("context", …) paths are safe; TestUtils.getClient() builds a fresh Client per call, so setFgaCacheUri can't leak between tests; getInfo(), getMissingContext() and getConditions() are all impl-guaranteed non-null; and connection-refused reaches the caller as an IOException via @SneakyThrows, so assertThrows(Exception.class, …) catches it.

No issues sniffed out — nothing actionable survived verification. Good bones! 🦴 Woof!


Declared coverage: FULL — 1/1 changed files reviewed.


Declared coverage: FULL — 1/1 changed files reviewed.

@yosiharan
yosiharan force-pushed the test/fga-live-coverage branch from 76ca0cb to 6c8f85a Compare August 10, 2026 12:23
@shuni-bot

shuni-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🐕 Review complete — View session on Shuni Portal 🐾

@shuni-bot shuni-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review after force-push — no actionable findings

The previously reviewed commit 76ca0cb is no longer reachable (force-pushed), so per the fallback I re-reviewed the full current PR diff instead of a delta. The result: the single changed file src/test/java/com/descope/sdk/mgmt/impl/FGALiveTest.java has the identical blob (f6c0a8d) as the version reviewed in the prior round — only the commits were rewritten (rebase onto the updated base). +315 lines, one new test file, no production code touched.

Because the base branch may have moved under the rebase, I re-verified the implementation assumptions this test depends on rather than trusting the earlier pass:

  • Cache routing surface is still exactly 6 getFgaUri call sitesFGAServiceImpl saveSchema/createRelations/deleteRelations/check (both overloads share the site at :129) and AuthzServiceImpl whoCanAccess (:214) / whatCanTargetAccess (:255). The 7 assertThrows in testBadFgaCacheUrlFailsOnlyTheRoutedCalls map onto those, and the 5 calls asserted to still succeed (loadSchema, dryRunSchema, saveResourcesDetails, loadResourcesDetails, resourceRelations) all still go through the non-routed getUri.
  • ManagementsBase.getFgaUri still normalizes with StringUtils.removeEnd(fgaCacheUri, "/"), so the deliberate trailing slash at FGALiveTest.java:230 exercises the intended path.
  • Non-null guarantees the assertions rely on hold: check always populates info and info.missingContext (FGAServiceImpl.java:136-139), and loadSchema always sets conditions before any early return (:72).
  • Model field orders still match every positional constructor callFGARelation(resource, resourceType, relation, target, targetType), FGACheckResult(allowed, relation, info), FGAResourceDetails(resourceId, resourceType, displayName), FGAResourceIdentifier(resourceId, resourceType), FGACheckInfo(direct, conditional, missingContext, …). Relation.getResource() used by reachesDoc1 is still the resource id field.
  • TestUtils.getClient() still returns a fresh Client.builder() per call, so setFgaCacheUri in the cache-tier tests cannot leak into fgaService/authzService from @BeforeEach.
  • EnvironmentUtils.getFgaCacheURL() / FGA_CACHE_URL_ENV_VAR and the AuthzService context overloads (whoCanAccess(4-arg), whatCanTargetAccess(2-arg)) all exist as called.

I also re-derived all 17 GDrive check expectations from the DSL independently (including the group1#member userset resolution for folder2 and the parent.owner chain giving u9 can_create/can_edit on folder1) — every expectation is correct, and expect()'s hardcoded folder/user types hold for all 17.

Correctness, security, error handling, and test-quality checks all pass. The shared-project trade-offs (no schema cleanup, ServerCommonException in onExceptions) are explicitly documented in the class javadoc and the inline comment at :97-98, so I'm not re-raising them.


Declared coverage: FULL — 1/1 changed files reviewed.

Base automatically changed from feat/fga-cache-url to main August 10, 2026 13:01
yosiharan and others added 4 commits August 10, 2026 16:01
Mirrors integrationtests/tests/fga_test.go against a real project: the GDrive
schema round trip with all its check expectations, the ABAC context cases
(conditional allow and deny, missing context, condition on a permission), and
loadSchema returning version and conditions.

Adds coverage the Go suite does not have: dry-run, ABAC context through the
authz queries, and FGA cache routing. Cache routing is covered three ways -
pointing the cache URL at the API host (everything must keep working), pointing
it at a dead port (only the six routed calls must fail, the rest must still
succeed), and against a real cache when DESCOPE_FGA_CACHE_URL is set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The server normalizes targetType on the tuple it echoes back from check, so
compare the fields that identify the relation instead of the whole object.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
These tests replace the project's FGA schema, which is shared with the other
live tests, so a schema-dependent call can hit a transient server error. Retry
the same way the suite already retries rate limits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Delete the schema once for the class instead of after every test, and fold the
authz-context and loadSchema assertions into the ABAC test that already saves
that schema. Halves the schema mutations per run, which is what was making
concurrent CI runs fail each other's schema-dependent live tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
yosiharan and others added 4 commits August 10, 2026 16:01
Another live suite reads loadSchema().getName() without a null guard, so
leaving the last schema in place is safer than deleting it, and it drops the
remaining schema churn. Also assert the non-routed authz call does not throw
rather than that its list is non-null, which the wire can legitimately omit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drop the retry suspend to 10s so a deterministic failure does not idle for
minutes across the JDK matrix, guard the deny-path whoCanAccess against a null
list from the wire, and share one round-trip helper between the two cache tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Asserting non-null passed on an empty list, so it would have passed even if the
context were ignored. Contrast admin and non-admin the way the whoCanAccess
assertions above do.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@yosiharan
yosiharan force-pushed the test/fga-live-coverage branch from 6c8f85a to 9e73bbb Compare August 10, 2026 13:01
@shuni-bot

shuni-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🐕 Review complete — View session on Shuni Portal 🐾

@shuni-bot shuni-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐕 Shuni's Review

Adds FGALiveTest — live coverage for the FGA surface: the GDrive schema round trip with all 17 check expectations, ABAC context (including missingContext), dry-run, and three tiers of FGA cache-URL routing. One new test file, +315 lines, no production code touched.

Sniffed out 0 issues. Good bones! 🦴

The previously reviewed commit 6c8f85a was force-pushed away, so I reviewed the full current PR diff. Two things changed since the last round: the base is now origin/main (the three upstream PRs landed), and the file blob is unchanged (f6c0a8d). Since the base moved, I re-verified the implementation this test pins rather than trusting the earlier pass:

  • Routing surface is still exactly 6 getFgaUri sites (FGAServiceImpl :50/:94/:107/:129, AuthzServiceImpl :214/:255), matching the 7 assertThrows; the 5 calls asserted to survive a dead cache URL all still use getUri.
  • ManagementsBase.getFgaUri still trims the trailing slash, so the deliberate + "/" at :230 exercises normalization; UriUtils.getUri composes the leading slash correctly.
  • check still guarantees non-null info and missingContext; loadSchema still sets conditions before every return path.
  • Positional model constructors still match field order (FGARelation, FGACheckResult, FGAResourceDetails, FGAResourceIdentifier), and mapOf still returns a mutable map for the request.put("context", …) paths.
  • TestUtils.getClient() still builds a fresh Client per call and does not read DESCOPE_FGA_CACHE_URL (only Config does), so setFgaCacheUri cannot leak into the @BeforeEach services.
  • @RetryingTest(onExceptions = …) is already used with the pinned junit-pioneer 1.9.1 in UserServiceImplTest.

I also re-derived all 17 GDrive expectations from the DSL independently — including the group1#member userset on folder2 and the parent.owner chain giving u9 can_create/can_edit on folder1 — all correct. The shared-project trade-offs (no schema cleanup, ServerCommonException retries) are documented in the class javadoc and at :97-98, so I'm not re-raising them. Woof!


Declared coverage: FULL — 1/1 changed files reviewed.

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